Hibernate JPA 關聯關系
關聯關系從整體上分為單向關聯和雙向關聯:
- 單向關聯:隻需從一端通路另一端,如教師Teacher可通路學生Student,則Teacher實體需要包含類型為Student的屬性
- 雙向關聯:兩端均可互相通路,如教師Teacher可通路學生Student,學生Student也可通路教師Teacher,兩個實體均需要包含類型為對方的屬性
1、一對一關聯映射
1.1、單向一對一
單向1-1:需要在控制關系的一方實體中使用注解 @OneToOne 和 @JoinColumn 标注類型為對方的屬性。如
Person
端為控制關系的一方,隻需要在
Person
控制方加這兩個注解即可。反端既不用配置屬性字段也不用配置注解
單向一對一是關聯關系映射中最簡單的一種,簡單地說就是可以從關聯的一方去查詢另一方,卻不能反向查詢。我們用下面的例子來舉例說明,清單 1 中的 Person 實體類和清單 2 中的 Address 類就是這種單向的一對一關系,我們可以查詢一個 Person 的對應的 Address 的内容,但是我們卻不能由一個 Address 的值去查詢這個值對應的 Person
- tb_person_address(外鍵表):
,street,city,country,address_id
person_id
- tb_person(主鍵表):
,usernameperson_id
清單 1:單向一對一關系的擁有端(正端)
@Data
@Entity
@Table(name = "tb_person")
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "person_id")
private Long personId;
private String username;
/**
* @JoinColumn
* name: person_address表外鍵字段名(資料庫字段名)
* referencedColumnName: person表主鍵字段(資料庫字段名)
*/
@OneToOne
@JoinColumn(name = "address_id",referencedColumnName = "address_id")
private PersonAddress personAddress;
}
清單 2:單向一對一關系的被擁有端(反端)
@Data
@Entity
@Table(name = "tb_person_address")
public class PersonAddress implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "address_id")
private Long addressId;
private String country;
private String city;
}
執行代碼測試:
/**
* 運作之前,修改hibernate.hbm2ddl.auto=create
* 單向一對一查詢
*/
@Test
public void testOneToOne(){
EntityManager entityManager = JpaUtils.getEntityManager();
// 開啟事務
entityManager.getTransaction().begin();
// 儲存位址資料
PersonAddress address = new PersonAddress();
address.setCountry("中國");
address.setCity("廣州");
entityManager.persist(address);
// 儲存使用者資訊
Person person = new Person();
person.setUsername("Sam");
person.setPersonAddress(address);
entityManager.persist(person);
// 送出更新事務
entityManager.getTransaction().commit();
// 查詢擁有端(外鍵表端),先清理緩存
entityManager.clear();
System.out.println(entityManager.find(Person.class,person.getPersonId()));
entityManager.close();
}
檢視日志1(日志分為兩段,1為建表):
Hibernate:
create table tb_person (
person_id bigint not null auto_increment,
username varchar(255),
address_id bigint,
primary key (person_id)
) engine=InnoDB
Hibernate:
create table tb_person_address (
address_id bigint not null auto_increment,
city varchar(255),
country varchar(255),
primary key (address_id)
) engine=InnoDB
Hibernate:
alter table tb_person
add constraint FKcep21ttdy3yuo1f56giyatasf
foreign key (address_id)
references t_person_address (address_id)
檢視日志2(日志分為兩段,2為資料插入與查詢):
Hibernate: // 資料插入
insert
into
t_person_address
(city, country)
values
(?, ?)
Hibernate:
insert
into
t_person
(address_id, username)
values
(?, ?)
Hibernate: // 資料查詢
select
person0_.person_id as person_i1_5_0_,
person0_.address_id as address_3_5_0_,
person0_.username as username2_5_0_,
personaddr1_.address_id as address_1_6_1_,
personaddr1_.city as city2_6_1_,
personaddr1_.country as country3_6_1_
from
tb_person person0_
left outer join
tb_person_address personaddr1_
on person0_.address_id=personaddr1_.address_id
where
person0_.person_id=?
Person(personId=1, username=Sam, address=PersonAddress(addressId=1, country=中國, city=廣州))
1.2、雙向一對一
清單 3:單向一對一關系的擁有端
@Data
@Entity
@Table(name = "tb_person_address")
public class PersonAddress implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "address_id")
private Long addressId;
private String country;
private String city;
/**
* @OneToOne
* mappedBy:放棄外鍵維護。mappedBy 隻有在雙向關聯的時候設定。值為對方類引用本類的屬性名
*/
@OneToOne(mappedBy = "personAddress")
private Person person;
}
清單 4:雙向一對一關系中的接受端
@Data
@Entity
@Table(name = "tb_person")
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "person_id")
private Long personId;
private String username;
/**
* @JoinColumn
* name: person_address表外鍵字段名(資料庫字段名)
* referencedColumnName: person表主鍵字段(資料庫字段名)
*/
@OneToOne
@JoinColumn(name = "address_id",referencedColumnName = "address_id")
private PersonAddress personAddress;
}
執行代碼測試:
/**
* 運作之前,修改hibernate.hbm2ddl.auto=create
* 雙向一對一查詢
*/
@Test
public void testOneToOne2(){
EntityManager entityManager = JpaUtils.getEntityManager();
// 開啟事務
entityManager.getTransaction().begin();
PersonAddress personAddress = new PersonAddress();
personAddress.setCountry("中國");
personAddress.setCity("廣州");
entityManager.persist(personAddress);
Person person = new Person();
person.setUsername("Sam");
person.setPersonAddress(personAddress);
entityManager.persist(person);
// 送出事務
entityManager.getTransaction().commit();
// 查詢擁有端(外鍵表端)先清理緩存
entityManager.clear();
System.out.println(entityManager.find(PersonAddress.class,personAddress.getAddressId()));
// 查詢被擁有端(主鍵表端)
System.out.println(entityManager.find(Person.class,person.getPersonId()));
entityManager.close();
}
檢視日志(建表語句就不重複列印了,因為是一摸一樣的):
// ...省略插入語句和查詢語句
java.lang.StackOverflowError
at java.lang.Long.toString(Long.java:396)
at java.lang.Long.toString(Long.java:1032)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at OneToOne.Person.toString(Person.java:8)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at OneToOne.PersonAddress.toString(PersonAddress.java:8)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at OneToOne.Person.toString(Person.java:8)
at java.lang.String.valueOf(String.java:2994)
...
現在在查詢步驟中會出現死循環(後面解決)
2、一對多關聯映射
2.1、單向一對多
單向1-N:需要在控制關系的一方實體中使用注解 @OneToMany 和 @JoinColumn 标注類型為對方的集合屬性(有兩種方式:一種是隻加@OneToMany、另一種是 @OneToMany + @JoinColumn)
- tb_people(外鍵表):
,name,people_id
people_id
- tb_people_phone(主鍵表):
,type,phonephone_id
清單 5:單向一對多關系的擁有端(一方、主鍵表方、主表)
@Data
@Entity
@Table(name = "tb_people")
public class People {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "people_id")
private Long peopleId;
private String name;
/**
* @OneToMany
* cascade = CascadeType.ALL:級聯儲存、更新、删除、重新整理
* fetch = FetchType.LAZY :延遲加載
* @JoinColumn
* name 指定外鍵列,這裡注意指定的是people_id,實際上是為了外鍵表定義的字段。該字段在PeoplePhone類必須定義
*/
@OneToMany
@JoinColumn(name="people_id")
private List<PeoplePhone> peoplePhones = new ArrayList<>();
}
清單 6:單向一對多關系的接收端(多方、外鍵表方、從表)
@Data
@Entity
@Table(name = "tb_people_phone")
public class PeoplePhone {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "phone_id")
private Long phoneId;
private String type;
private String phone;
}
測試代碼:
/**
* 單向一對多查詢
*/
@Test
public void testOneToMany(){
EntityManager entityManager = JpaUtils.getEntityManager();
entityManager.getTransaction().begin(); // 開啟事務
PeoplePhone peoplePhoneA = new PeoplePhone();
peoplePhoneA.setType("date_time");
peoplePhoneA.setPhone("13011113333");
PeoplePhone peoplePhoneB = new PeoplePhone();
peoplePhoneB.setType("mobile");
peoplePhoneB.setPhone("0208514851");
entityManager.persist(peoplePhoneA);
entityManager.persist(peoplePhoneB);
People people = new People();
people.setName("Sam");
people.getPeoplePhones().add(peoplePhoneA);
people.getPeoplePhones().add(peoplePhoneB);
entityManager.persist(people);
entityManager.getTransaction().commit(); // 送出更新事務
// 查詢擁有端(主鍵表端,注意:單向一對多是配置在 一方/擁有端)
System.out.println(entityManager.find(People.class,people.getPeopleId()));
entityManager.close();
}
檢視日志1:(建表語句)
Hibernate:
create table tb_people (
people_id bigint not null auto_increment,
name varchar(255),
primary key (people_id)
) engine=InnoDB
Hibernate:
create table tb_people_phone (
phone_id bigint not null auto_increment,
phone varchar(255),
type varchar(255),
people_id bigint,
primary key (phone_id)
) engine=InnoDB
Hibernate:
alter table tb_people_phone
add constraint FKnied6axrmqsyl5olnjywa7set
foreign key (people_id)
references t_people (people_id)
檢視日志2:
Hibernate:
insert
into
tb_people_phone
(phone, type)
values
(?, ?)
Hibernate:
insert
into
tb_people_phone
(phone, type)
values
(?, ?)
Hibernate:
insert
into
tb_people
(name)
values
(?)
Hibernate:
update
tb_people_phone
set
people_id=?
where
phone_id=?
Hibernate:
update
tb_people_phone
set
people_id=?
where
phone_id=?
People(peopleId=1, name=Sam, peoplePhones=[PeoplePhone(phoneId=1, type=date_time, phone=13011113333), PeoplePhone(phoneId=2, type=mobile, phone=0208514851)])
mysql> select * from tb_people;
+-----------+------+
| people_id | name |
+-----------+------+
| 1 | Sam |
+-----------+------+
1 row in set (0.01 sec)
mysql> select * from tb_people_phone;
+----------+-------------+-----------+-----------+
| phone_id | phone | type | people_id |
+----------+-------------+-----------+-----------+
| 1 | 13011113333 | date_time | 1 |
| 2 | 0208514851 | mobile | 1 |
+----------+-------------+-----------+-----------+
2 rows in set (0.02 sec)
2.2、單向多對一
單向N-1:需要在控制關系的一方實體中使用注解 @ManyToOne 和 @JoinColumn 标注類型為對方的屬性。
清單 7:單向多對一關系的擁有端(多方、外鍵表方、從表)
@Data
@Entity
@Table(name = "tb_people_phone")
public class PeoplePhone {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "phone_id")
private Long phoneId;
private String type;
private String phone;
/**
* @JoinColumn
* name 指定外鍵列
* referencedColumnName: people 表主鍵字段(資料庫字段名)
*/
@ManyToOne
@JoinColumn(name="people_id", referencedColumnName = "people_id")
private People people;
}
清單 8:單向多對一關系的被擁有端(一方、主鍵表方、主表)
@Data
@Entity
@Table(name = "tb_people")
public class People {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "people_id")
private Long peopleId;
private String name;
}
測試代碼:
/**
* 單向多對一查詢
*/
@Test
public void testManyToOne(){
EntityManager entityManager = JpaUtils.getEntityManager();
entityManager.getTransaction().begin();// 開啟事務
// 先儲存主鍵(被維護端)資料
People people = new People();
people.setName("Sam");
entityManager.persist(people);
// 然後儲存外鍵(維護端)資料
PeoplePhone peoplePhoneA = new PeoplePhone();
peoplePhoneA.setType("date_time");
peoplePhoneA.setPhone("13011113333");
peoplePhoneA.setPeople(people);
PeoplePhone peoplePhoneB = new PeoplePhone();
peoplePhoneB.setType("mobile");
peoplePhoneB.setPhone("0208514851");
peoplePhoneB.setPeople(people);
entityManager.persist(peoplePhoneA);
entityManager.persist(peoplePhoneB);
entityManager.getTransaction().commit();// 送出更新事務
// 查詢擁有端(外鍵表端,注意:單向多對一是配置在多方外鍵擁有端)
entityManager.clear();
System.out.println(entityManager.find(PeoplePhone.class,peoplePhoneA.getPhoneId()));
System.out.println(entityManager.find(PeoplePhone.class,peoplePhoneB.getPhoneId()));
entityManager.close();
}
檢視日志:
Hibernate: // 插入資料
insert
into
tb_people
(name)
values
(?)
Hibernate: // 插入資料
insert
into
tb_people_phone
(people_id, phone, type)
values
(?, ?, ?)
Hibernate: // 插入資料
insert
into
tb_people_phone
(people_id, phone, type)
values
(?, ?, ?)
Hibernate: // 查詢擁有端
select
peoplephon0_.phone_id as phone_id1_4_0_,
peoplephon0_.people_id as people_i4_4_0_,
peoplephon0_.phone as phone2_4_0_,
peoplephon0_.type as type3_4_0_,
people1_.people_id as people_i1_3_1_,
people1_.name as name2_3_1_
from
tb_people_phone peoplephon0_
left outer join
tb_people people1_
on peoplephon0_.people_id=people1_.people_id
where
peoplephon0_.phone_id=?
PeoplePhone(phoneId=1, type=date_time, phone=13011113333, people=People(peopleId=1, name=Sam))
Hibernate: // 查詢擁有端
select
peoplephon0_.phone_id as phone_id1_4_0_,
peoplephon0_.people_id as people_i4_4_0_,
peoplephon0_.phone as phone2_4_0_,
peoplephon0_.type as type3_4_0_,
people1_.people_id as people_i1_3_1_,
people1_.name as name2_3_1_
from
tb_people_phone peoplephon0_
left outer join
tb_people people1_
on peoplephon0_.people_id=people1_.people_id
where
peoplephon0_.phone_id=?
PeoplePhone(phoneId=2, type=mobile, phone=0208514851, people=People(peopleId=1, name=Sam))
mysql> select * from tb_people;
+-----------+------+
| people_id | name |
+-----------+------+
| 1 | Sam |
+-----------+------+
1 row in set (0.02 sec)
mysql> select * from tb_people_phone;
+----------+-------------+-----------+-----------+
| phone_id | phone | type | people_id |
+----------+-------------+-----------+-----------+
| 1 | 13011113333 | date_time | 1 |
| 2 | 0208514851 | mobile | 1 |
+----------+-------------+-----------+-----------+
2.3、雙向一對多
雙向1-N(N-1):1的一端需要使用注解
@OneToMany
标注類型為對方的集合屬性,同時指定
mappedBy
屬性表示1的一端不控制關系,N的一端則需要使用注解@ManyToOne 和 @JoinColumn 标注類型為對方的屬性。
清單 9:雙向一對多關系的接受端(一方、主鍵表方、主表)
@Data
@Entity
@Table(name = "tb_people")
public class People {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "people_id")
private Long peopleId;
private String name;
/**
* mappedBy:指定從表實體類中引用主表對象的名稱。指明這端不控制關系
* targetEntity:指定多的一方的類的位元組碼
* cascade :指定要使用的級聯操作
* fetch :指定是否采用延遲加載
* orphanRemoval:是否使用孤兒删除
*/
@OneToMany(
mappedBy = "people",
targetEntity = PeoplePhone.class,
cascade = CascadeType.ALL,
fetch = FetchType.LAZY)
private List<PeoplePhone> peoplePhones = new ArrayList<>();
}
清單 10:雙向一對多關系的發出端(多方、外鍵表方、從表)
@Data
@Entity
@Table(name = "tb_people_phone")
public class PeoplePhone {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "phone_id")
private Long phoneId;
private String type;
private String phone;
/**
* @JoinColumn
* name 指定外鍵列
* referencedColumnName: people 表主鍵字段(資料庫字段名)
*/
@ManyToOne // cascade | fetch | targetEntity 都是可選
@JoinColumn(name="people_id", referencedColumnName = "people_id")
private People people;
}
測試代碼:
/**
* 雙向一對多查詢
*/
@Test
public void testManyToOne(){
EntityManager entityManager = JpaUtils.getEntityManager();
entityManager.getTransaction().begin();// 開啟事務
// 先儲存主鍵(被維護端)資料
People people = new People();
people.setName("Sam");
entityManager.persist(people);
// 然後儲存外鍵(維護端)資料
PeoplePhone peoplePhoneA = new PeoplePhone();
peoplePhoneA.setType("date_time");
peoplePhoneA.setPhone("13011113333");
peoplePhoneA.setPeople(people);
PeoplePhone peoplePhoneB = new PeoplePhone();
peoplePhoneB.setType("mobile");
peoplePhoneB.setPhone("0208514851");
peoplePhoneB.setPeople(people);
entityManager.persist(peoplePhoneA);
entityManager.persist(peoplePhoneB);
entityManager.getTransaction().commit();// 送出更新事務
entityManager.clear();
// 查詢被擁有端(主鍵表端)
System.out.println(entityManager.find(People.class,people.getPeopleId()));
// 查詢擁有端(外鍵表端,注意:單向多對一是配置在多方外鍵擁有端)
System.out.println(entityManager.find(PeoplePhone.class,peoplePhoneA.getPhoneId()));
System.out.println(entityManager.find(PeoplePhone.class,peoplePhoneB.getPhoneId()));
entityManager.close();
}
檢視日志(建表語句就不重複列印了,因為是一摸一樣的):
// ...省略插入語句和查詢語句
java.lang.StackOverflowError
at java.lang.StringBuilder.append(StringBuilder.java:136)
at OneToMany.PeoplePhone.toString(PeoplePhone.java:7)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at java.util.AbstractCollection.toString(AbstractCollection.java:462)
at org.hibernate.collection.internal.PersistentBag.toString(PersistentBag.java:538)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at OneToMany.People.toString(People.java:9)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at OneToMany.PeoplePhone.toString(PeoplePhone.java:7)
...
現在在查詢步驟中會出現死循環(後面解決)
3、多對多關聯映射
3.1、單向多對多
單向N-N:需要在控制關系的一方實體中使用注解@ManyToMany 和 @JoinTable标注類型為對方的屬性,這裡應該是一個集合屬性
清單 11:單向多對多關系的發出端(擁有端)
@Data
@Entity
@Table(name = "tb_role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "role_id")
private Long roleId;
@Column(name = "role_name")
private String roleName;
/**
* @JoinTable:
* name:中間表名稱
* joinColumns 中間表對應本類的資訊
* @JoinColumn;
* name:本類的外鍵字段(中間表的資料庫字段)
* referencedColumnName:本類與外鍵(表)對應的主鍵(本類的主鍵字段)
* inverseJoinColumns 中間表對應對方類的資訊
* @JoinColumn:
* name:對方類的外鍵(中間表的資料字段)
* referencedColumnName:對方類與外鍵(表)對應的主鍵(對方類的主鍵字段)
*/
@ManyToMany
@JoinTable(name="tb_role_permission", // 中間表明
joinColumns=@JoinColumn(
name="role_id", // 本類的外鍵
referencedColumnName = "role_id"), // 本類與外鍵(表)對應的主鍵
inverseJoinColumns=@JoinColumn(
name="permission_id", // 對方類的外鍵
referencedColumnName = "permission_id")) // 對方類與外鍵(表)對應的主鍵
private Set<Permission> permissions = new HashSet<>();
}
清單 11:單向多對多關系的接收端(被擁有端)什麼都不用配置
@Data
@Entity
@Table(name = "tb_permission")
public class Permission {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "permission_id")
private Long permissionId;
@Column(name = "permission_name")
private String permissionName;
}
測試代碼:
/**
* 單向多對多
*/
@Test
public void testManyToMany(){
EntityManager entityManager = JpaUtils.getEntityManager();
// 開啟事務
entityManager.getTransaction().begin();
// 增權重限資料
Permission permissionA = new Permission();
permissionA.setPermissionName("增加");
Permission permissionB = new Permission();
permissionB.setPermissionName("查詢");
entityManager.persist(permissionA);
entityManager.persist(permissionB);
entityManager.persist(permissionB);
// 增加角色資料
Role role = new Role();
role.setRoleName("網絡管理者");
role.getPermissions().add(permissionA);
role.getPermissions().add(permissionB);
entityManager.persist(role);
// 送出更新事務
entityManager.getTransaction().commit();
// 查詢角色資訊
entityManager.clear();
System.out.println(entityManager.find(Role.class,role.getRoleId()));
entityManager.close();
}
檢視日志1:(建表語句)
Hibernate:
create table tb_permission (
permission_id bigint not null auto_increment,
permission_name varchar(255),
primary key (permission_id)
) engine=InnoDB
Hibernate:
create table tb_role (
role_id bigint not null auto_increment,
role_name varchar(255),
primary key (role_id)
) engine=InnoDB
Hibernate:
create table tb_role_permission (
role_id bigint not null,
permission_id bigint not null,
primary key (role_id, permission_id)
) engine=InnoDB
Hibernate:
alter table tb_role_permission
add constraint FKjobmrl6dorhlfite4u34hciik
foreign key (permission_id)
references tb_permission (permission_id)
Hibernate:
alter table tb_role_permission
add constraint FK90j038mnbnthgkc17mqnoilu9
foreign key (role_id)
references tb_role (role_id)
檢視日志2:(資料插入與查詢)
Hibernate:
insert
into
tb_permission
(permission_name)
values
(?)
Hibernate:
insert
into
tb_permission
(permission_name)
values
(?)
Hibernate:
insert
into
tb_role
(role_name)
values
(?)
Hibernate:
insert
into
tb_role_permission
(role_id, permission_id)
values
(?, ?)
Hibernate:
insert
into
tb_role_permission
(role_id, permission_id)
values
(?, ?)
Hibernate:
select
role0_.role_id as role_id1_6_0_,
role0_.role_name as role_nam2_6_0_
from
tb_role role0_
where
role0_.role_id=?
Hibernate:
select
permission0_.role_id as role_id1_0_0_,
permission0_.permission_id as permissi2_0_0_,
permission1_.permission_id as permissi1_3_1_,
permission1_.permission_name as permissi2_3_1_
from
tb_role_permission permission0_
inner join
tb_permission permission1_
on permission0_.permission_id=permission1_.permission_id
where
permission0_.role_id=?
Role(roleId=1, roleName=網絡管理者, permissions=[Permission(permissionId=2, permissionName=查詢), Permission(permissionId=1, permissionName=增加)])
mysql> select * from tb_role;
+---------+------------+
| role_id | role_name |
+---------+------------+
| 1 | 網絡管理者 |
+---------+------------+
1 row in set (0.05 sec)
mysql> select * from tb_permission;
+---------------+-----------------+
| permission_id | permission_name |
+---------------+-----------------+
| 1 | 增加 |
| 2 | 查詢 |
+---------------+-----------------+
2 rows in set (0.06 sec)
mysql> select * from tb_role_permission;
+---------+---------------+
| role_id | permission_id |
+---------+---------------+
| 1 | 3 |
| 1 | 4 |
+---------+---------------+
2 rows in set (0.05 sec)
PS:單向多對多反過來配置到另一個類也是一樣。隻需要把本類的外鍵和對方類外鍵調換一下即可。
3.2、雙向多對多
發出端(擁有端)代碼不變
@Data
@Entity
@Table(name = "t_role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "role_id")
private Long roleId;
@Column(name = "role_name")
private String roleName;
/**
* @JoinTable:
* name:中間表名稱
* joinColumns 中間表對應本類的資訊
* @JoinColumn;
* name:本類的外鍵字段(中間表的資料庫字段)
* referencedColumnName:本類與外鍵(表)對應的主鍵(本類的主鍵字段)
* inverseJoinColumns 中間表對應對方類的資訊
* @JoinColumn:
* name:對方類的外鍵(中間表的資料字段)
* referencedColumnName:對方類與外鍵(表)對應的主鍵(對方類的主鍵字段)
*/
@ManyToMany
@JoinTable(name="t_role_permission", // 中間表明
joinColumns=@JoinColumn(
name="role_id", // 本類的外鍵
referencedColumnName = "role_id"), // 本類與外鍵(表)對應的主鍵
inverseJoinColumns=@JoinColumn(
name="permission_id", // 對方類的外鍵
referencedColumnName = "permission_id")) // 對方類與外鍵(表)對應的主鍵
private Set<Permission> permissions = new HashSet<>();
}
接收端(被擁有端)代碼增加了@ManyToMany 注解和
mappedBy
屬性
@Data
@Entity
@Table(name = "t_permission")
public class Permission {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "permission_id")
private Long permissionId;
@Column(name = "permission_name")
private String permissionName;
/**
* @ManyToMany
* mappedBy:對方類應用該類的屬性名,指明這端不控制關系
* cascade | fetch | targetEntity 為可選屬性
*/
@ManyToMany(mappedBy = "permissions")
private Set<Role> role = new HashSet<>();
}
測試代碼:
/**
* 單向/雙向 多對多查詢代碼
*/
@Test
public void testManyToManyFind(){
EntityManager entityManager = JpaUtils.getEntityManager();
System.out.println(entityManager.find(Role.class,1L));
System.err.println("--------------華麗的分割線-------------------");
System.out.println(entityManager.find(Permission.class,1L));
entityManager.close();
}
可以發現單向查詢依舊正常,而雙向查詢 依舊會有死循環問題(該問題在SpringDataJPA篇章中解決)