spring-data-jpa 一对多,多对一,多对多
首先介绍几个主要用到的注解
@ManyToOne
多对一@ManyToMany
多对多@OneToMany
一对多@JoinColumn
两表之间的关联@JsonIgnoreProperties
忽略属性(避免Jason套娃)
比如我有两张表:customer
,bill
,一个customer数据对应bill中多条数据,两表通过 customer.id
和 bill.customer_id
关联
实体如下
@Entity
@Table(name = "bill")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Bill {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Column(name = "customer_id")private Long customerId;@Column(name = "bill_no")private String billNo;@Column(name = "bill_amount")private float billAmount;@Column(name = "bill_date")private LocalDate billDate;
}
@Entity
@Table(name = "customer")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Customer {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Column(name = "customer_name")private String customerName;@Column(name = "customer_age")private Integer customerAge;@Column(name = "customer_account")private String customerAccount;@Column(name = "customer_password")private String customerPassword;
}
为了使两张表关联起来,需要在实体中加入关联的逻辑
customer.java 中加入
@OneToMany( targetEntity = Bill.class, fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
@JsonIgnoreProperties(value = {"customer"})
@JoinColumn(name = "customer_id",referencedColumnName = "id",insertable = false,updatable = false )
private List<Bill> bills;
Bill.java 中加入
@ManyToOne(targetEntity = Customer.class, fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
@JsonIgnoreProperties(value = {"bills"})
@JoinColumn(name = "customer_id",referencedColumnName = "id",insertable = false,updatable = false )
private Customer customer;
下面说说如何配置使用
@ManyToOne,@ManyToMany,@OneToMany
意义就不解释了,通过注解名称就可以看懂,只说明属性
-
targetEntity
类型:Class<?>
指定关系的另一端实体的类型。比如上述代码中,customer实体类与bill实体关联,所以我在customer类的bills属性上的注解@OneToMany的targetEntity属性赋值Bill.class;这个属性是可选的,如果不指定,spring-data-jpa 将根据字段的类型来确定目标实体类,建议写上。
-
fetch
类型:javax.persistence.FetchType
指定加载策略:立即加载(FetchType.EAGER)和 延迟加载(FetchType.LAZY)
-
立即加载:加载主实体时,其关联的实体或集合也会同时被加载。这种策略通过减少数据库查询次数来提高性能,因为它尝试在一个查询中获取所有关联实体的数据
-
延迟加载:加载主实体时,其关联的实体或集合不会立即被加载,而是在第一次访问关联属性时才进行加载。这种策略可以减少不必要的数据库查询,因为只有在实际需要数据时才会进行加载
使用延迟加载,我遇到了一个坑,这里顺便记录,有时一条a表数据对应几十万条b表数据,使用立即加载肯定是不合适的,故而用了延迟加载,使用延迟加载的时候,报错如下
No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.ArrayList[0]->com.train.spr.entities.Bill["customer"]->com.train.spr.entities.Customer$HibernateProxy$g4FqEJel["hibernateLazyInitializer"])
如果你也遇到同样的问题,用如下方式解决
在实体类名上加一句代码,如下@JsonIgnoreProperties(value = {"hibernateLazyInitializer"}) public class Bill { // 实现(略) }
-
-
cascade
类型:javax.persistence.CascadeType[]
级联操作,定义哪些持久化操作(如保存、更新、删除)应该被级联到关系的另一端实体。
CascadeType
是枚举类,可选值如下CascadeType.ALL
:表示所有的持久化操作都会级联到关系的另一端实体。这包括:PERSIST、MERGE、REMOVE、REFRESH 和 DETACHCascadeType.PERSIST
:当实体的对象被创建时,相关的实体也会被级联保存CascadeType.MERGE
:当实体被合并到当前持久化上下文时,相关的实体也会被级联合并CascadeType.REMOVE
:当实体被删除时,相关的实体也会被级联删除(删customer表中一条数据,jpa自动删除bill表中关联数据)CascadeType.REFRESH
:当实体被刷新时,相关的实体也会被级联刷新CascadeType.DETACH
:当实体被分离出持久化上下文时,相关的实体也会被级联分离
@JoinColumns 关联关系集合
若两张表之间通过多个字段相关联,比如A表和B表通过A.CREATE_DATE = B.CREATE_DATE AND A.ID = B.A_ID
相关联,使用 @JoinColumns
包裹 @JoinColumn
集合
@JoinColumn 关联关系
定义两个实体之间以什么字段相关联;属性如下
-
name
类型:string
指定本表关联其他表的本表列名称
-
referencedColumnName
类型:string
指定本表关联其他表的其他表列名称(其他表的列名称也叫做外键)
-
unique
类型:bool
指定外键列是否具有唯一性约束(可选,默认false),拿我给的例子来说,一个customer可以对应多个bill,但是一个bill仅能对应一个customer,所以bill类中customer属性的@JoinColumn注解的属性改为true,表示customer中仅能有唯一一个实体与之对应
-
nullable
类型:bool
指定外键列是否可以为 NULL(可选,默认true),表示列可以为 NULL
-
insertable
类型:bool
指定外键列是否在插入操作时可被插入(可选,默认true)
-
updatable
类型:bool
指定外键列是否在更新操作时可被更新(可选,默认true)
-
columnDefinition
类型:string
指定外键列的 SQL 类型定义。这允许你自定义列的 SQL 类型和大小等详细信息(可选)
-
table
类型:string
指定外键列所在的表的名称。这在多表继承的场景中使用
-
foreignKey
类型:String
指定外键约束的名称。如果未指定,spring-data-jpa根据实体关系自动生成一个外键约束名称(可选)
@JsonIgnoreProperties JSON属性忽略
如果实体类的关联属性上不加此注解,那么你查出来的json数据结构绝对是“套娃”;属性如下
-
value
类型:string[]
指定忽略对方实体类中的某个属性转换为json,比如我上述例子中 customer.java 中有属性 bills,而bill.java中又有customer,所以他俩得互相忽略对方实体类中对自己引用的属性名,故而customer实体中忽略bill中的customer,bill实体中忽略customer中的bills。这么说有点抽象哈,不过你自己试一试就很轻松能明白了
-
ignoreUnknown
类型:boolean
ignoreUnknown 属性只适用于从 JSON 数据到 Java 对象的反序列化过程,当设置为 true 时,如果 JSON 中包含未在 Java 类中定义的属性,这些属性将被忽略。这有助于处理 JSON 数据中可能存在的额外字段,而不会抛出异常(可选)
-
allowGetters
类型:boolean
当设置为 true 时,即使属性没有 setter 方法,也可以通过 getter 方法访问。这通常用于只读属性(可选)
-
allowSetters
类型:boolean
当设置为 true 时,即使属性没有 getter 方法,也可以通过 setter 方法设置值。这通常用于只写属性(可选)
暂且写到这儿,后续有总结再补