关联关系从整体上分为单向关联和双向关联:
单向1-1:需要在控制关系的一方实体中使用注解 @OneToOne 和 @JoinColumn 标注类型为对方的属性。如Person
端为控制关系的一方,只需要在Person
控制方加这两个注解即可。反端既不用配置属性字段也不用配置注解
单向一对一是关联关系映射中最简单的一种,简单地说就是可以从关联的一方去查询另一方,却不能反向查询。我们用下面的例子来举例说明,清单 1 中的 Person 实体类和清单 2 中的 Address 类就是这种单向的一对一关系,我们可以查询一个 Person 的对应的 Address 的内容,但是我们却不能由一个 Address 的值去查询这个值对应的 Person
address_id
,street,city,country,person_id
person_id
,username清单 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=广州))
清单 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)
...
现在在查询步骤中会出现死循环(后面解决)
单向1-N:需要在控制关系的一方实体中使用注解 @OneToMany 和 @JoinColumn 标注类型为对方的集合属性(有两种方式:一种是只加@OneToMany、另一种是 @OneToMany + @JoinColumn)
people_id
,name,people_id
phone_id
,type,phone清单 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)
单向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 |
+----------+-------------+-----------+-----------+
双向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)
...
现在在查询步骤中会出现死循环(后面解决)
单向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:单向多对多反过来配置到另一个类也是一样。只需要把本类的外键和对方类外键调换一下即可。
发出端(拥有端)代码不变
@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篇章中解决)