一、简介
本章我们学习springData Jpa 进阶操作,,项目我们还是用的上篇的项目开始 传送门
二、实体关系映射
多对多、一对多、多对一、一对一关系的深度实例解析
1.作用简化编程操作。把冗余的操作交给底层框架来处理。
例如,如果我要给一位新入学的学生添加一位新的老师。而这个老师又是新来的,在学生数据库与教师数据库中均不存在对应的数据。那么我需要先在教师数据库中保存新来的老师的数据,同时在学生数据库中保存新学生的数据,然后再给两者建立关联。
而如果我们使用了实体关系映射,我们只需要将该新教师实体交给该学生实体,然后保存该学生实体即可完成。
2.多对多
举例:学生和课程的关系。一位学生,会修多门课程;而一门课程,也会被多位学生修习。此时,双方的关系即为多对多关系。拥有多对多关系的两个实体将会有一个中间表来记录两者之间的关联关系。
- Student
//表明是一个实体
@Entity
//对于数据库t_user表
@Table(name ="t_student")
@Data
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name ="cName",length = 64)
private String sName;
@ManyToMany(cascade= CascadeType.ALL,fetch=FetchType.LAZY)
private Set<Course> courses = new HashSet<>();//这里直接new了空对象方便直接设置
}
- Course
//表明是一个实体
@Entity
//对于数据库t_user表
@Table(name ="t_course")
@Data
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name ="cName",length = 64)
private String cName;
@ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY,mappedBy="courses")//cascade(级联关系)fetch(加载策略)mappedBy(声明关系的维护方)mappedBy出现在哪一方意味着哪一方不用维护外键关系了
private Set<Student> students= new HashSet<>();//这里直接new了空对象方便直接设置
}
/*
mappedBy声明于关系的被维护方,声明的值为关系的维护方的关系对象属性名。
在实例中,mappedBy被声明于Course类中,其值为Student类中的Set对象"courses"。即,Student为关系维护方,Course为被维护方。
*/
注意:在多对多关系时 使用@Data注解会引发内存溢出问题,用以下注解代替
@NoArgsConstructor:生成空构造函数
@Getter:生成get方法
@Setter: 生成set方法
fetch管读取,cascade管增删改,关于fetch解释如下
1.FetchType.LAZY:懒加载,加载一个实体时,定义懒加载的属性不会马上从数据库中加载。
2.FetchType.EAGER:急加载,加载一个实体时,定义急加载的属性会立即从数据库中加载。
3.@OneToMany默认的是LAZY,@ManyToOne默认是EAGER
3.测试
@RestController
@RequestMapping("/test")
public class StudentController {
@Autowired
private StudentRepository studentRepository;
@Autowired
private CourseRepository courseRepository;
/**
* 仅将被维护方对象添加进维护方对象Set中
* 保存维护方对象
*/
@GetMapping("/insertS")
@Transactional
public void insertStudent() {
//运行后发现数据库创建了3表,其中有一张时中间表
Student s = new Student();
s.setStudentName("二狗");
Course c = new Course();
c.setCourseName("语文");
s.getCourses().add(c);
//由于操作对象是维护方,成功地在student、course以及中间表student_courses中分别添加了数据
studentRepository.save(s);
}
/**
* 仅将维护方对象添加进被维护方对象Set中
* 保存被维护方对象
*/
@GetMapping("/insertC")
@Transactional
public void insertCourses() {
Student s = new Student();
s.setStudentName("三汪");
Course c = new Course();
c.setCourseName("英语");
c.getStudents().add(s);
//操作对象在这里换成了被维护方。不负众望,出问题了。保存的时候,student表和course表倒是都成功地插入了数据,但是中间表中,并未产生对两者数据的关联。
courseRepository.save(c);
}
/**
* 将双方对象均添加进双方Set中
* 保存被维护方对象
*/
@GetMapping("/insertCS")
@Transactional
public void insertCoursesAndStudent() {
Student s = new Student();
s.setStudentName("一晌");
Course c = new Course();
c.setCourseName("数学");
s.getCourses().add(c);
c.getStudents(