系列文章目录
文章目录
前言
正常开发中,经常会遇到多表关联操作,一下将对各个关系进行解释说:
实体关系 | 注解 |
---|---|
一对一 | @OneToOne |
一对多,多对一 | @OneToMany,@ManyToOne |
多对多 | @ManyToMany |
1. @OneToOne
1.1属性说明:
- targetEntityClass:表示默认关联的实体类型,默认为当前标注的实体类
- cascade:关联属性,这个属性定义了当前类对象操作了之后,级联对象的操作。级联可取值如下:
- 不定义:对关系表不会产生任何影响
- CascadeType.PERSIST:级联新建
- CascadeType.REMOVE:级联删除
- CascadeType.REFRESH:级联刷新
- CascadeType.MERGE:级联更新
- CascadeType.ALL:表示同事选择了上述的所有级联操作
- fetch:FetchType 类型的属性
- FetchType.EAGER:表示关系类在主类加载的时候同时加载
- FetchType.LAZY:表示关系类在被访问时才加载,默认值是 FetchType.LAZY
- mappedBy:定义在双向 @OneToOne,双向 @OneToMany,双向 @ManyToMany,值为子表的属性
- orphanRemoval:指定在删除一方(One)数据的同时是否删除掉多方(Many)的数据。
默认为 false
,不删除多方(Many)数据;仅仅将一方(One)和连接表的数据进行删除
1.2 实体说明
实体 People :用户。
实体 Address:家庭住址。
People 和 Address 是一对一的关系。
@OneToOne 作用:用于映射一对一关系
属性:
cascade:配置级联操作。
@JoinColumn 作用:声明关联关系
属性:
name:当前表的字段(用于存储外键的信息)
referencedColumnName:引用表对应的字段,如果不注明,默认就是引用表的主键
用户实体 People
@Entity
@Table(name = "people")
public class People {
@Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "org.hibernate.id.UUIDGenerator")
private String uuid;
private String name;//姓名
private String sex;//性别
private Timestamp birthday;//出生日期
//People是关系的维护端,当删除 people,会级联删除 address
@OneToOne(cascade=CascadeType.ALL)
//people中的address_id字段参考address表中的id字段
@JoinColumn(name = "address_id", referencedColumnName = "id")
private Address address;//地址
}
地址实体 Address
mappedBy:
只有OneToOne,OneToMany,ManyToMany上才有mappedBy属性,ManyToOne不存在该属性;
mappedBy标签一定是定义在被拥有方的,他指向拥有方;
mappedBy的含义,应该理解为,拥有方能够自动维护跟被拥有方的关系,当然,如果从被拥有方,通过手工强行来维护拥有方的关系也是可以做到的;
mappedBy跟joinColumn/JoinTable总是处于互斥的一方,可以理解为正是由于拥有方的关联被拥有方的字段存在,拥有方才拥有了被拥有方。mappedBy这方定义JoinColumn/JoinTable总是失效的,不会建立对应的字段或者表。
@Entity
@Table(name = "address")
public class Address {
@Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "org.hibernate.id.UUIDGenerator")
private String uuid;
private String zipcode;//邮政编码
private String address;//地址
//如果不需要根据Address级联查询People,可以注释掉
// @OneToOne(mappedBy = "address", cascade = {CascadeType.MERGE, CascadeType.REFRESH}, optional = false)
// private People people;
}
2. @OneToMany,@ManyToOne
2.1 实体说明
实体 Author:作者。
实体 Article:文章。
Author 和 Article 是一对多关系(双向)。
JPA使用@OneToMany和@ManyToOne来标识一对多的双向关联。一端(Author)使用@OneToMany,多端(Article)使用@ManyToOne。
在JPA规范中,一对多的双向关系由多端(Article)来维护。就是说多端(Article)为关系维护端,负责关系的增删改查。一端(Author)则为关系被维护端,不能维护关系。
一端(Author)使用@OneToMany注释的mappedBy="author"属性表明Author是关系被维护端。
作者实体:Author
@Entity
public class Author {
@Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "org.hibernate.id.UUIDGenerator")
private String uuid;
private String name;//姓名
@OneToMany(mappedBy = "author",cascade=CascadeType.ALL,fetch=FetchType.LAZY)
//级联保存、更新、删除、刷新;延迟加载。当删除用户,会级联删除该用户的所有文章
//拥有mappedBy注解的实体类为关系被维护端
//mappedBy="author"中的author是Article中的author属性
private List<Article> articleList;//文章列表
}
文章实体:Article
@Entity
public class Article {
@Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "org.hibernate.id.UUIDGenerator")
private String uuid;
private String title;
private String content;//文章全文内容
@ManyToOne(cascade={CascadeType.MERGE,CascadeType.REFRESH},optional=false)//可选属性optional=false,表示author不能为空。删除文章,不影响用户
@JoinColumn(name="author_id")//设置在article表中的关联字段(外键)
private Author author;//所属作者
}
3. @ManyToMany
实体 Student:学生。
实体 Student:课程。
Student和 Student是多对多关系(双向)。
3.1 实体说明
@ManyToMany 作用:用于映射多对多关系
属性:
cascade:配置级联操作。
fetch:配置是否采用延迟加载。
targetEntity:配置目标的实体类。映射多对多的时候不用写。
@JoinTable 作用:针对中间表的配置
属性:
name:配置中间表的名称
joinColumns:中间表的外键字段关联当前实体类所对应表的主键字段
inverseJoinColumns:中间表的外键字段关联对方表的主键字段
@JsonIgnoreProperties(“studentList”):类级别的注解。
指定的字段不会被序列化和反序列化。在多对多中主要为避免进入死循环
学生实体 Student
@Data
@Table
@Entity
public class Student {
@Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "org.hibernate.id.UUIDGenerator")
private String id;
private String name;
private Sex sex;
private int age;
@ManyToMany
@JsonIgnoreProperties("studentList")
@JoinTable(name = "STUDENT_COURSE", joinColumns = @JoinColumn(name = "STUDENT_ID"),inverseJoinColumns = @JoinColumn(name = "COURSE_ID"))
private List<Course> courseList;
}
课程实体 Course
@ManyToMany属性:mappedBy
1.只有OneToOne,OneToMany,ManyToMany上才有mappedBy属性,ManyToOne不存在该属性;
2.mappedBy标签一定是定义在the owned side(被拥有方的),他指向theowning side(拥有方);
3.关系的拥有方负责关系的维护,在拥有方建立外键。所以用到@JoinColumn
4.mappedBy跟JoinColumn/JoinTable总是处于互斥的一方
@Data
@EqualsAndHashCode(callSuper = false)
@Entity
public class Course{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid",strategy = "org.hibernate.id.UUIDGenerator")
private String id;
private String name; // 课程名称
@JsonIgnoreProperties("courseList")
@ManyToMany(mappedBy = "courseList")
private List<Student> studentList;
}
4. CascadeType(各种级联操作)详解。
关于级联操作的说明,下文是我找到一篇比较好的说明
作者:三汪
链接:https://www.jianshu.com/p/e8caafce5445
5. 解决无限递归,进入死循环问题。
当一对多,多对一,多对多情况时,为了避免进入死循环,有两个解决方案:
- 破坏某一方的 toString() 方法即可,最好是破坏多的一方的 toString() 方法,建议使用
@ToString(exclude = "属性名")
- 使用注解解决。以下对多个注解进行使用说明:
@JsonBackReference
@JsonManagedReference
@JsonIgnore
@JsonIgnoreProperties
- @JsonBackReference标注的属性在序列化(serialization,即将对象转换为json数据)时,会被忽略(即结果中的json数据不包含该属性的内容)。
- @JsonManagedReference标注的属性则会被序列化。
@JsonBackReference和@JsonManagedReference:这两个标注通常配对使用,通常用在父子关系中
- @JsonIgnore 直接忽略某个属性,以断开无限递归,序列化或反序列化均忽略。
- @JsonIgnoreProperties 忽略某个对象中的属性
allowSetters = true 指定的逻辑属性@JsonIgnoreProperties将参与 JSON 反序列化,但不参与序列化。
allowGetters = true 指定的逻辑属性@JsonIgnoreProperties将参与 JSON 序列化但不参与反序列化。