《JPA 从入门到精通》系列包含以下文章:
- Java 持久层 API:JPA
- 认识 JPA 的接口
- JPA 的查询方式
- 基于 JPA 开发的文章管理系统(CRUD)
- 关系映射开发(一):一对一映射
- 关系映射开发(二):一对多映射
- 关系映射开发(三):多对多映射
关系映射开发(三):多对多映射
- 1.创建实体
- 1.1 创建 Student 实体
- 1.2 创建 Teacher 实体
- 2.创建测试
在 多对多 关联关系中,只能通过 中间表 的方式进行映射,不能通过增加外键来实现。
注解 @ManyToMany 用于关系的发出端和接收端。关系的发出端定义一个集合类型的接收端的字段属性,关系的接收端不需要做任何定义。
1.创建实体
1.1 创建 Student 实体
package com.example.demo.entity;import lombok.Data;import javax.persistence.*;
import java.util.List;
import java.util.Set;@Entity
@Data
public class Student {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private long id;private String name;@Column(columnDefinition = "enum('male','female')")private String sex;@ManyToMany(fetch = FetchType.LAZY)@JoinTable(name = "teacher_student", joinColumns = {@JoinColumn(name = "s_id")}, inverseJoinColumns = {@JoinColumn(name = "t_id")})private Set<Teacher> teachers;
}
1.2 创建 Teacher 实体
package com.example.demo.entity;import lombok.Data;import javax.persistence.*;
import java.util.List;
import java.util.Set;@Data
@Entity
public class Teacher {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private long id;private String name;@ManyToMany(fetch = FetchType.LAZY)/*** Description:* 1、关系两边都作为主控;* 2、joinColumns 中 @JoinColumn(name="t_id") 其中 t_id 为 JoinTable 中的外键,由于 Student 和 Teacher 的主键都为 id 这边就省略 referencedColumnName="id"。*/@JoinTable(name = "teacher_student", joinColumns = {@JoinColumn(name = "t_id")}, inverseJoinColumns = {@JoinColumn(name = "s_id")})private Set<Student> students;
}
在 多对多 关系中需要注意以下几点:
- 关系两方都可以作为主控。
- 在
joinColumns
的@JoinColumn(name="t_id")
中,t_id
为 JoinTable 中的外键。由于 Student 和 Teacher 的主键都为id
,所以这里省略了referencedColumnName="id"
。 - 在设计模型之间的级联关系时,要考虑好应该采用何种级联规则。
- 如果设置
cascade = CascadeType.PERSIST
,则在执行save
时会调用onPersist()
方法。这个方法会递归调用外联类(Student 或 Teacher)的onPersist()
进行级联新增。但因为值已经添加了,所以会报detached entity passed to persist
错误,将级联操作取消(去掉cascade = CascadeType.PERSIST
)即可。
2.创建测试
Service 和 Repository 层在《关系映射开发(一):一对一映射》已经讲过,这里并没有区别,所以不再赘述,直接进入测试层的代码编写。
package com.example.demo.entity;import com.example.demo.repository.StudentRepository;
import com.example.demo.repository.TeacherRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;import java.util.HashSet;
import java.util.Set;import static org.junit.Assert.*;@SpringBootTest
@RunWith(SpringRunner.class)
public class ManyToManyTest {@Autowiredprivate StudentRepository studentRepository;@Autowiredprivate TeacherRepository teacherRepository;@Testpublic void add() {Set<Teacher> teachers = new HashSet<>();Set<Student> students = new HashSet<>();Student student1 = new Student();student1.setName("张三");students.add(student1);studentRepository.save(student1);Student student2 = new Student();student2.setName("李四");students.add(student2);studentRepository.save(student2);Teacher teacher1 = new Teacher();teacher1.setName("皮皮老师");teacher1.setStudents(students);teachers.add(teacher1);teacherRepository.save(teacher1);}
}
运行测试类,在控制器中输岀如下结果:
🚀 对于双向 ManyToMany 关系,注解 @ManyToMany 用于关系的发出端和接收端。另外,关系的接收端需要设置
@ManyToMany(mappedBy='集合类型发出端实体的字段名称')
。