SpringDataJpa使用
水沝淼㵘 人气:0SpringDataJpa的使用 -- 一对一、一对多、多对多 关系映射
本文主要讲述 @OneToOne、@OneToMany、@ManyToOne、@ManyToMany
这四个关系映射注解的使用,以及其对应的级联关系
有四张表,分别是:学生表、家长表、教室表、教师表,它们的关联关系如下:
- 学生 对 家长:一对一
- 学生 对 教室:多对一
- 学生 对 教师:多对多
项目依赖
必要的依赖有:mysql驱动、SpringBootWeb、SpringBootStarter、SpringDataJpa、SpringBootText、druid,其它的依赖可以按需添加。 如:lombok、
pom.xml 必要依赖
<!-- Spring Boot 都使用 2.4.5,看个人习惯 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.4.5</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.4.5</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-test</artifactId> <version>2.4.5</version> <scope>runtime</scope> </dependency> <!-- Spring Data JPA 使用 2.6.1,看个人习惯 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> <version>2.6.1</version> </dependency> <!-- mysql 驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.25</version> <scope>runtime</scope> </dependency> <!-- druid 数据库连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.24</version> </dependency>
pom.xml 可选依赖
<!-- Spring 热部署 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <version>2.4.5</version> <scope>runtime</scope> <optional>true</optional> </dependency> <!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.20</version> <optional>true</optional> </dependency>
项目配置
application.yaml
spring: # DataSource datasource: driver-class-name: com.mysql.cj.jdbc.Driver username: 账号 password: 密码 # serverTimezone可以设置为北京时间GMT%2B8、上海时间Asia/Shanghai或者香港时间Hongkong url: jdbc:mysql://localhost:3306/cloudtext?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8 # DataBase Connection -- JPA jpa: # 显示 SQL 语句 show-sql: true # 不允许在视图阶段 执行 sql open-in-view: false # 建议设置一个不常用的,避免冲突 server: # 项目端口号 port: 8092 # 项目路径 servlet: context-path: /jpa
sql文件(MySQL版)
cloudText.sql 仅结构
-- ---------------------------- -- Table structure for classroom -- ---------------------------- DROP TABLE IF EXISTS `classroom`; CREATE TABLE `classroom` ( `class_room_id` tinyint unsigned NOT NULL AUTO_INCREMENT COMMENT '班级主键', `class_room_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '班级名称', `class_room_location` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '班级位置', `class_room_capacity` int unsigned DEFAULT NULL COMMENT '班级人数', `class_room_grade` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '班级年级', PRIMARY KEY (`class_room_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin; -- ---------------------------- -- Table structure for patriarch -- ---------------------------- DROP TABLE IF EXISTS `patriarch`; CREATE TABLE `patriarch` ( `patriarch_id` tinyint unsigned NOT NULL AUTO_INCREMENT COMMENT '家长主键', `patriarch_name` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '家长姓名', `patriarch_age` int unsigned DEFAULT NULL COMMENT '家长年龄', `patriarch_sex` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '家长性别', `patriarch_phone` varchar(11) COLLATE utf8_bin DEFAULT NULL COMMENT '家长电话', `patriarch_address` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '家长住址', PRIMARY KEY (`patriarch_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin; -- ---------------------------- -- Table structure for student -- ---------------------------- DROP TABLE IF EXISTS `student`; CREATE TABLE `student` ( `student_id` tinyint unsigned NOT NULL AUTO_INCREMENT COMMENT '学生主键', `student_name` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '学生姓名', `student_age` int unsigned DEFAULT NULL COMMENT '学生年龄', `student_sex` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '学生性别', `patriarch_id` tinyint unsigned NOT NULL COMMENT '家长外键', `class_room_id` tinyint unsigned DEFAULT NULL COMMENT '班级外键', PRIMARY KEY (`student_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin; -- ---------------------------- -- Table structure for student_teacher -- ---------------------------- DROP TABLE IF EXISTS `student_teacher`; CREATE TABLE `student_teacher` ( `student_id` tinyint unsigned NOT NULL COMMENT '学生主键', `teacher_id` tinyint unsigned NOT NULL COMMENT '教师主键', PRIMARY KEY (`student_id`,`teacher_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin; -- ---------------------------- -- Table structure for teacher -- ---------------------------- DROP TABLE IF EXISTS `teacher`; CREATE TABLE `teacher` ( `teacher_id` tinyint unsigned NOT NULL AUTO_INCREMENT COMMENT '教师主键', `teacher_name` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '教师姓名', `teacher_age` int unsigned DEFAULT NULL COMMENT '教师年龄', `teacher_sex` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '教师性别', `teacher_course` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '教师教授科目', PRIMARY KEY (`teacher_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin;
级联关系简述
- ALL -- 级联
所有
操作 - PERSIST -- 级联
持久化
操作 - MERGE -- 级联
合并
操作 - REMOVE -- 级联
删除
操作 - REFRESH -- 级联
刷新
操作(调用refresh方法才会刷新) - DETACH -- 级联
分离
操作(2.0版本才有)
参考:《Spring Boot 整合——JPA 数据模型关联操作(一对一、一对多、多对多)》
@OneToOne 一对一 关系映射
一对一关系的双方必须有且仅有一方放弃维护,在 @OneToOne 中添加mappedBy属性表示放弃维护。
学生类(Student.java):维护方 家长类(Patriarch.java):被维护方
Student.java (主键和非外键属性)
package com.ljm.exmaple.entity; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.OneToOne; import javax.persistence.Table; /** * 学生 类 * * @author LJM */ @Entity @Table(name = "STUDENT") public class Student { /** * 学生 id */ @Id @Column(name = "student_id", nullable = false) @GeneratedValue(strategy = GenerationType.IDENTITY) private Long studentId; /** * 学生 姓名 */ @Column(name = "student_name", nullable = false) private String studentName; /** * 学生 年龄 */ @Column(name = "student_age", nullable = false) private Short studentAge; /** * 学生 性别 */ @Column(name = "student_sex", nullable = false) private String studentSex; /** 省略 Constructor、Getter、Setter、toString 方法 **/ }
Patriarch.java (主键和非外键属性)
package com.ljm.exmaple.entity; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.OneToOne; import javax.persistence.Table; /** * 家长 类 * * @author LJM */ @Entity @Table(name = "PATRIARCH") public class Patriarch { /** * 家长 id */ @Id @Column(name = "patriarch_id", nullable = false) @GeneratedValue(strategy = GenerationType.IDENTITY) private Long patriarchId; /** * 家长 姓名 */ @Column(name = "patriarch_name", nullable = false) private String patriarchName; /** * 家长 年龄 */ @Column(name = "patriarch_age") private Short patriarchAge; /** * 家长 性别 */ @Column(name = "patriarch_sex", nullable = false) private String patriarchSex; /** * 家长 电话 */ @Column(name = "patriarch_phone", nullable = false, length = 11) private String patriarchPhone; /** * 家长 住址 */ @Column(name = "patriarch_address", nullable = false) private String patriarchAddress; /** 省略 Constructor、Getter、Setter、toString 方法 **/ }
1.无中间表,维护方添加外键,被维护方添加对应项
cascade = CascadeType.ALL
表示:级联关系。
fetch = FetchType.EAGER
表示:预加载。
name = "patriarch_id"
指定外键。
nullable = false
表示:不能为空。
unique = true
表示:不能重复。
mappedBy = "patriarch"
:添加了这个属性表示放弃维护。
维护方的属性添加到 Student.java 中。
被维护方的属性添加到 Patriarch.java 中。
/** * 一对一 * 家长 外键 * 维护方 * 使用 CascadeType.ALL 亦可,区别不大 */ @OneToOne(cascade = {CascadeType.MERGE, CascadeType.REMOVE}, fetch = FetchType.EAGER) @JoinColumn(name = "patriarch_id", nullable = false, unique = true) private Patriarch patriarch; /** * 一对一 * 被维护方(对应项) * 使用 CascadeType.ALL 亦可,区别不大 */ @OneToOne(mappedBy = "patriarch", cascade = {CascadeType.REFRESH, CascadeType.MERGE, CascadeType.REMOVE}, fetch = FetchType.EAGER) private Student student; /** 注意,外键及对应项的 Getter、Setter 不要忘了 */
2.无中间表,维护方添加外键,被维护方不添加对应项
cascade = CascadeType.ALL 表示:级联关系。
fetch = FetchType.EAGER 表示:预加载。
name = "patriarch_id" 指定外键。
nullable = false 表示:不能为空。
unique = true 表示:不能重复。
维护方的属性添加到 Student.java 中。
/** * 一对一 * 家长 外键 * 维护方 * 使用 CascadeType.ALL 亦可,区别不大 */ @OneToOne(cascade = {CascadeType.MERGE, CascadeType.REMOVE}, fetch = FetchType.EAGER) @JoinColumn(name = "patriarch_id", nullable = false, unique = true) private Patriarch patriarch; /** * 一对一 * 被维护方(对应项),不需要添加对应项 * 使用 CascadeType.ALL 亦可,区别不大 */ /** * @OneToOne(mappedBy = "patriarch", cascade = {CascadeType.REFRESH, CascadeType.MERGE, CascadeType.REMOVE}, fetch = FetchType.EAGER) * private Student student; */ /** 注意,外键的 Getter、Setter 不要忘了 */
3.有中间表,维护方不添加外键,被维护方不添加对应项
暂缺,这不是本文重点,以后可能会添加。
不需要使用关系注解 @OneToOne、@OneToMany、@ManyToOne、@ManyToMany,没有外键。
@OneToMany、@ManyToOne 一对多 关系映射
一对多关系中,多方必须维护外键,一方一般不维护(我在某篇文章中看到:说一方也可以维护外键,我还未验证)。
学生类(Student.java):维护方
教室类(ClassRoom.java):被维护方
Student.java (主键和非外键属性)
package com.ljm.exmaple.entity; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.OneToOne; import javax.persistence.Table; /** * 学生 类 * * @author LJM */ @Entity @Table(name = "STUDENT") public class Student { /** * 学生 id */ @Id @Column(name = "student_id", nullable = false) @GeneratedValue(strategy = GenerationType.IDENTITY) private Long studentId; /** * 学生 姓名 */ @Column(name = "student_name", nullable = false) private String studentName; /** * 学生 年龄 */ @Column(name = "student_age", nullable = false) private Short studentAge; /** * 学生 性别 */ @Column(name = "student_sex", nullable = false) private String studentSex; /** 省略 Constructor、Getter、Setter、toString 方法 **/ }
ClassRoom.java (主键和非外键属性)
package com.ljm.exmaple.entity; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.OneToMany; import javax.persistence.Table; /** * 教室 类 * * @author LJM */ @Entity @Table(name = "CLASSROOM") public class ClassRoom { /** * 教室 id */ @Id @Column(name = "classRoom_id", nullable = false) @GeneratedValue(strategy = GenerationType.IDENTITY) private Long classRoomId; /** * 教室 名称 */ @Column(name = "classRoom_name", nullable = false) private String classRoomName; /** * 教室 位置 */ @Column(name = "classRoom_location", nullable = false) private String classRoomLocation; /** * 教室 容量 */ @Column(name = "classRoom_capacity", nullable = false) private Short classRoomCapacity; /** * 教室 所在的 班级的年级(小学到高中) */ @Column(name = "classRoom_grade", nullable = false) private String classRoomGrade; /** 省略 Constructor、Getter、Setter、toString 方法 **/ }
1.无中间表,多方维护并添加外键,一方被维护
cascade = CascadeType.XXX 表示:级联关系。
fetch = FetchType.EAGER 表示:预加载。
name = "classRoom_id" 指定外键。
nullable = false 表示:不能为空。
mappedBy = "classRoom" :添加了这个属性表示放弃维护,在一对多的一方用来指向多方的那个外键属性。
/** * 多对一 * 多方 * 教室外键 * 维护方,级联关系为 none(没有,默认) */ @JoinColumn(name = "classRoom_id", nullable = false) @ManyToOne(fetch = FetchType.EAGER) private ClassRoom classRoom; /** * 一对多 * 一方 * (被)维护方(对应项) * 被维护方代码 */ @OneToMany(mappedBy = "classRoom", cascade = {CascadeType.MERGE, CascadeType.REFRESH}, fetch = FetchType.EAGER) private List<Student> studentList; /** 注意,外键及对应项的 Getter、Setter 不要忘了 */
2.有中间表,多方维护,一方被维护
暂缺,这不是本文重点,以后可能会添加。
不需要使用关系注解 @OneToOne、@OneToMany、@ManyToOne、@ManyToMany,没有外键。
3.无中间表,多方维护,一方也维护
暂缺,待验证。
@ManyToMany 多对多 关系映射
多对多关系中,必须有且仅有一方放弃维护外键,需要新建中间表,但不需要写对应的实体类。
学生类(Student.java):维护方
教师类(Teacher.java):被维护方
Student.java (主键和非外键属性)
package com.ljm.exmaple.entity; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.ManyToOne; import javax.persistence.OneToOne; import javax.persistence.Table; /** * 学生 类 * * @author LJM */ @Entity @Table(name = "STUDENT") public class Student { /** * 学生 id */ @Id @Column(name = "student_id", nullable = false) @GeneratedValue(strategy = GenerationType.IDENTITY) private Long studentId; /** * 学生 姓名 */ @Column(name = "student_name", nullable = false) private String studentName; /** * 学生 年龄 */ @Column(name = "student_age", nullable = false) private Short studentAge; /** * 学生 性别 */ @Column(name = "student_sex", nullable = false) private String studentSex; /** 省略 Constructor、Getter、Setter、toString 方法 **/ }
Teacher.java (主键和非外键属性)
package com.ljm.exmaple.entity; import java.util.List; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.ManyToMany; import javax.persistence.Table; /** * 教师 类 * * @author LJM */ @Entity @Table(name = "TEACHER") public class Teacher { /** * 教师 id */ @Id @Column(name = "teacher_id", nullable = false) @GeneratedValue(strategy = GenerationType.IDENTITY) private Long teacherId; /** * 教师 姓名 */ @Column(name = "teacher_name", nullable = false) private String teacherName; /** * 教师 年龄 */ @Column(name = "teacher_age") private Integer teacherAge; /** * 教师 性别 */ @Column(name = "teacher_sex", nullable = false) private String teacherSex; /** * 教师 所教的 科目 */ @Column(name = "teacher_course", nullable = false) private String teacherCourse; /** 省略 Constructor、Getter、Setter、toString 方法 **/ }
fetch = FetchType.EAGER 表示:预加载。
name = "student_teacher" 指定关联关系表。
joinColumns = @JoinColumn(name = "student_id") 指定我方在关联表中对应的主键。
inverseJoinColumns = @JoinColumn(name = "teacher_id") 指定被维护方在关联表中对应的主键。
mappedBy = "teacherList" :添加了这个属性表示放弃维护,维护方用来指向另一方的那个外键属性。
/** * 多对多 * 维护方 * 教师 外键 * 级联关系为 none(没有,默认),双方互不干扰 */ @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "student_teacher", joinColumns = @JoinColumn(name = "student_id"), inverseJoinColumns = @JoinColumn(name = "teacher_id")) private List<Teacher> teacherList; /** * 多对多 * 被维护方 * 学生 外键(对应项) * 级联关系为 none(没有,默认),双方互不干扰 */ @ManyToMany(mappedBy = "teacherList", fetch = FetchType.EAGER) private List<Student> studentList; /** 注意,外键及对应项的 Getter、Setter 不要忘了 */
本文参考
加载全部内容