掃二維碼與項(xiàng)目經(jīng)理溝通
我們在微信上24小時(shí)期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
比如說一個(gè)學(xué)校有一個(gè)地址,一個(gè)地址只有一個(gè)學(xué)校。

那么我們可以按照如下方式來設(shè)計(jì)類:
@Data
@Entity
@Table(name = "t_address")
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer aid;
private String province;
private String city;
private String area;
private String phone;
@OneToOne(cascade = CascadeType.ALL)
private School school;
}
@Data
@Entity
@Table(name = "t_school")
public class School {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer sid;
private String name;
@OneToOne(cascade = CascadeType.ALL)
private Address address;
}
一對一的關(guān)系,可以只在 School 中維護(hù),也可以只在 Address 中維護(hù),也可以兩者都維護(hù),具體哪種,那就看需求了。
在上面的例子中,我們在 School 和 Address 中都通過 @OneToOne 注解來維護(hù)了一對一的關(guān)系。
cascade 用來配置級聯(lián)操作,有如下取值:
根據(jù)自己需求選擇合適的就行。
這樣,最終創(chuàng)建出來的 t_school 表和 t_address 表中,會分別多出來一個(gè)字段 address_aid 和 school_sid,這兩個(gè)字段都是外鍵,正是通過外鍵,將兩張表中不同的記錄關(guān)聯(lián)起來。
有的人可能不習(xí)慣這種自動(dòng)添加的字段,那也可以自定義該字段,反正該字段總是要有的,自定義的方式如下:
@Data
@Entity
@Table(name = "t_address")
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer aid;
private String province;
private String city;
private String area;
private String phone;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "sid",referencedColumnName = "sid")
private School school;
@Column(insertable = false,updatable = false)
private Integer sid;
}
@Data
@Entity
@Table(name = "t_school")
public class School {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer sid;
private String name;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "aid",referencedColumnName = "aid")
private Address address;
@Column(insertable = false,updatable = false)
private Integer aid;
}
在 Address 中自定義一個(gè) sid,并設(shè)置該字段不可添加和修改,然后通過 @JoinColumn 注解去指定關(guān)聯(lián)關(guān)系,@JoinColumn 注解中的 name 表示的是當(dāng)前類中的屬性名,referencedColumnName 表示的則是 School 類中對應(yīng)的屬性名。
在 School 類中做相似的操作。
最后啟動(dòng)項(xiàng)目去觀察 MySQL 中生成的表。
一個(gè)班級中有多個(gè)學(xué)生,而一個(gè)學(xué)生只屬于一個(gè)班級,我們可以這樣來定義實(shí)體類:
@Data
@Table(name = "t_student")
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer sid;
private String name;
@ManyToOne(cascade = CascadeType.ALL)
private Clazz clazz;
}
@Data
@Table(name = "t_clazz")
@Entity
public class Clazz {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer cid;
private String name;
@OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
private Liststudents;
}
Student 和 Clazz 的關(guān)系是多對一,用 @ManyToOne 注解,Clazz 和 Student 的關(guān)系是一對多,用 @OneToMany 注解。
Student 和 Clazz 的關(guān)系是多對一,將來的 t_student 表中會多出來一個(gè)屬性 clazz_cid,通過這個(gè)外鍵將 Student 和 Clazz 關(guān)聯(lián)起來。如果我們不想要自動(dòng)生成的 clazz_cid,那么也可以自定義,方式如下:
@Data
@Table(name = "t_student")
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer sid;
private String name;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "cid")
private Clazz clazz;
@Column(insertable = false,updatable = false)
private Integer cid;
}
定義一個(gè) cid 屬性,并設(shè)置為不可編輯和不可添加,然后通過 @JoinColumn 注解配置 cid 屬性為外鍵。
Clazz 和 Student 的關(guān)系是一對多,這個(gè)是通過一個(gè)自動(dòng)生成的第三張表來實(shí)現(xiàn)的,如下:
先來個(gè)一對一的添加測試,如下:
public interface SchoolRepository extends JpaRepository{
}
@SpringBootTest
class JpaOneToManyApplicationTests {
@Autowired
SchoolRepository schoolRepository;
@Test
void contextLoads() {
School school = new School();
school.setSid(1);
school.setName("哈佛大學(xué)");
Address address = new Address();
address.setAid(1);
address.setProvince("黑龍江");
address.setCity("哈爾濱");
address.setArea("某地");
address.setPhone("123456");
school.setAddress(address);
schoolRepository.save(school);
}
}
在這個(gè)測試過程中,關(guān)聯(lián)關(guān)系是由 t_school 一方來維護(hù)了,因此將來填充的外鍵是 t_school 中的 aid。添加結(jié)果如下圖:
t_school
t_address
這是一個(gè)簡單的添加案例。
更新也是調(diào)用 save 方法,更新的時(shí)候會先判斷這個(gè) id 是否存在,存在的話就更新,不存在就添加。
再來看班級的添加,如下:
public interface ClazzRepository extends JpaRepository{
}
@Autowired
ClazzRepository clazzRepository;
@Test
void test02() {
Clazz c = new Clazz();
c.setCid(1);
c.setName("三年級二班");
Liststudents = new ArrayList<>();
Student s1 = new Student();
s1.setSid(1);
s1.setName("javaboy");
students.add(s1);
Student s2 = new Student();
s2.setSid(2);
s2.setName("張三");
students.add(s2);
c.setStudents(students);
clazzRepository.save(c);
}
注意,添加的是班級,所以班級和學(xué)生之間關(guān)系就由第三張表來維護(hù),而不是由學(xué)生來維護(hù)。
再來一個(gè)簡單的查詢,假設(shè)我們現(xiàn)在想根據(jù)省份來搜索學(xué)校,如下:
public interface SchoolRepository extends JpaRepository{
ListfindSchoolByAddressProvince(String province);
}
@Autowired
SchoolRepository schoolRepository;
@Test
void test01() {
Listlist = schoolRepository.findSchoolByAddressProvince("黑龍江");
System.out.println("list = " + list);
}
松哥給大家捋一下 Spring Data 如何解析上面自定義的查詢方法:
上面這個(gè)寫法有一個(gè)小小的風(fēng)險(xiǎn),假設(shè) School 中剛好就有一個(gè)屬性叫做 addressProvince,那么此時(shí)的分析就會出錯(cuò)。所以,對于上面的查詢,我們也可以定義成如下方式:
public interface SchoolRepository extends JpaRepository{
ListfindSchoolByAddress_Province(String province);
}
此時(shí)就不會產(chǎn)生歧義了,系統(tǒng)就知道 province 是 address 的屬性了。
再來一個(gè)班級的查詢,如下:
public interface ClazzRepository extends JpaRepository{
}
@Test
void test03() {
Listlist = clazzRepository.findAll();
System.out.println("list = " + list);
}
如果在查詢的過程中,需要對學(xué)生進(jìn)行排序,可以添加如下屬性:
@Data
@Table(name = "t_clazz")
@Entity
public class Clazz {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer cid;
private String name;
@OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
@OrderBy("sid desc")
private Liststudents;
}
通過 @OrderBy("sid desc") 可以設(shè)置查詢的 student 排序。

我們在微信上24小時(shí)期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流