Springboot中jpa中一对多关系

前言

在使用jpa持久化框架中,经常要建立关系表,jpa提供级联注解,这里说说其中的一对多@OneToMany和多对一@ManyToOne关系

注解说明

 

css

代码解读

复制代码

@OneToMany注解来定义具有一对多的多值关联,比如学校和和学生的关系

 

css

代码解读

复制代码

@ManyToOne注解来定义具有多对一的多值关联,比如学生和和学校的关系

使用说明

(1)建立一个学校实体类

 

less

代码解读

复制代码

@Data @Entity @Table(name = "t_school") @Accessors(chain = true) public class School { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; @OneToMany(mappedBy = "school") private List<Student> studentList; }

(2)建立一个学生实体类

 

less

代码解读

复制代码

@Data @Entity @Table(name = "t_student") public class Student { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; @ManyToOne @JoinColumn(name = "school_id") private School school; }

备注:如果关系是双向的,则@OneToMany必须使用 mappedBy 元素来指定作为关系所有者的实体的关系字段或属性,否则jpa在自动建表的时候,会生成另外一张表

使用mappedBy映射之后,自动生成表只会生成两张表

@JoinColumn标识,外键关系维护在t_student数据库表

前提

Student和School中dao层存在

 

kotlin

代码解读

复制代码

@Repository public interface SchoolRepository extends JpaRepository<School, Long>, JpaSpecificationExecutor<School> { }

 

kotlin

代码解读

复制代码

@Repository public interface StudentRepository extends JpaRepository<Student, Long>, JpaSpecificationExecutor<Student> { }

controller写法
 

less

代码解读

复制代码

@Slf4j @RestController @RequestMapping("/school") public class SchoolController { @Autowired private SchoolRepository schoolRepository; @Autowired private StudentRepository studentRepository; @GetMapping("/save") public void save() { School school = new School(); school.setName("aaaaa"); schoolRepository.save(school); } @GetMapping("/save1") public void save1() { Student student = new Student(); student.setName("aaaa"); studentRepository.save(student); } /** * 保存学生和学校之间的外键关系 */ @GetMapping("/save2") public void save2(@RequestParam("studentId") Long studentId, @RequestParam("schoolId") Long schoolId) { studentRepository.findById(studentId).ifPresent(e -> schoolRepository.findById(schoolId).ifPresent(x -> { e.setSchool(x); studentRepository.save(e); })); } /** * 断开学生和学校之间的外键关系 */ @GetMapping("/save3") public void save3(@RequestParam("id") Long id) { studentRepository.findById(id).ifPresent(e -> { e.setSchool(null); studentRepository.save(e); }); } /** * 删除学生数据 */ @GetMapping("/deleteStudentById") public void deleteStudentById(@RequestParam("id") Long id) { studentRepository.deleteById(id); } /** * 删除学校数据 */ @GetMapping("/deleteSchoolById") public void deleteSchoolById(@RequestParam("id") Long id) { schoolRepository.deleteById(id); } }

(1)调用save接口,保存学校数据,数据为:

(2)调用save1接口,保存学生数据,数据为:

 (3)调用save2关联两张表外键关系

(4)调用save3接口,可以断开两者之间的外键关系

(5)调用deleteStudentById接口,可以单独删除学生表数据

 这时候学生表数据不见了,学校数据还在

(6)但这时候,如果学校和学生数据还存在外键关联,这时候删除学校数据,则会报错

 报存在关键关系,不给删除,这个时候,我们要删除学校数据怎么办?就是先断开学校和学生的外键关系,然后再删除学校数据

 

less

代码解读

复制代码

/** * 断开学生和学校之间的外键关系 */ @GetMapping("/deleteSchoolById1") public void deleteSchoolById1(@RequestParam("id") Long id) { schoolRepository.findById(id).ifPresent(e -> { e.getStudentList().forEach(s -> { s.setSchool(null); studentRepository.save(s); }); }); schoolRepository.deleteById(id); }

image.png

image.png

 备注:

 

sql

代码解读

复制代码

@OneToMany注解有个orphanRemoval字段,用于标识删除一方(One)数据的同时是否删除掉多方(Many)的数据,默认是false,当设置为true的时候,删除学校,就不会报错了,就会把学生关联数据一起删除

image.png

 将orphanRemoval设置为true,这个时候,删除学校数据,就会把学生表一起删除

image.png

image.png

 

arduino

代码解读

复制代码

@ManyToOne有个optional字段,关联是否为可选,如果设置为 false,则必须始终存在非空关系。默认为 true

CascadeType字段

CascadeType有多个类型

 

arduino

代码解读

复制代码

CascadeType.ALL:所有操作都会级联执行,包括保存(persist)、更新(merge)、删除(remove)等。 CascadeType.PERSIST:级联保存操作。 CascadeType.MERGE:级联更新操作。 CascadeType.REMOVE:级联删除操作。 CascadeType.REFRESH:级联刷新操作。 CascadeType.DETACH:级联脱管操作。

以以上School实体类为例,

 

less

代码解读

复制代码

@Data @Entity @Table(name = "t_school") @Accessors(chain = true) public class School { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; @OneToMany(mappedBy = "school", cascade = CascadeType.ALL) private List<Student> studentList; }

Student实体类

 

less

代码解读

复制代码

@Data @Entity @Table(name = "t_student") public class Student { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; @ManyToOne @JoinColumn(name = "school_id") private School school; }

就代表School的增删改查,影响Student类,这个时候,可以级联保存了

controller写法
 

less

代码解读

复制代码

@Slf4j @RestController @RequestMapping("/student") public class StudentController { @Autowired private SchoolRepository schoolRepository; @Autowired private StudentRepository studentRepository; @GetMapping("/save") public void save() { School school = new School(); school.setName("hello"); Student student = new Student(); student.setName("aa1"); List<Student> studentList = new ArrayList<>(10); studentList.add(student); studentList.forEach(e->{ e.setSchool(school); }); school.setStudentList(studentList); schoolRepository.save(school); } /** * 删除学校数据 */ @GetMapping("/deleteSchoolById") public void deleteSchoolById(@RequestParam("id") Long id) { schoolRepository.deleteById(id); } /** * 删除学生数据 */ @GetMapping("/deleteStudentById") public void deleteStudentById(@RequestParam("id") Long id) { studentRepository.deleteById(id); } }

(1)执行save接口,会同时保存SchoolStudent数据,

image.png

image.png

备注:这里需要注意的是,要把School实体类塞到Student实体类,否则不会级联保存

(2)执行deleteSchoolById接口,会同时删除SchoolStudent数据。因为School使用了ALL,也就是所有操作都会级联影响Student数据

image.png

 (3)执行deleteStudentById接口,只会删除Student数据,因为Student没有级联

image.png

(4)如果想断开学生表外键,可以使用

 

less

代码解读

复制代码

/** * 断开学生表外键 */ @GetMapping("/hello") public void hello(@RequestParam("id") Long id) { schoolRepository.findById(id).ifPresent(e -> { e.getStudentList().forEach(s -> { s.setSchool(null); studentRepository.save(s); }); }); }

这个时候再执行接口删除,就不会删除学生表了,但是要注意的是,要分两个接口调用

(5)如果想学生表增删等,也影响学校表,那就一起级联

 

less

代码解读

复制代码

@Data @Entity @Table(name = "t_student") public class Student { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; private String name; @ManyToOne(cascade = CascadeType.ALL) @JoinColumn(name = "school_id") private School school; }

总结

jpa中的一对多和多对一如果相对熟悉的话,可以推荐使用,但是一般在开发过程中,因人而异,大部分不会采用外键级联表,这种根据自己技术决定

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值