JPA中Grandparent Entity级联保存时如何避免写入静态数据表C
报错根本原因
保存A时,A配置的Cascade.ALL会级联触发关联B实体的持久化操作。若此时B关联的C实体是游离态/临时态(没有被当前JPA持久化上下文管理),JPA会默认尝试将C写入静态表,从而触发写入权限、唯一约束等类报错。
可落地解决方案
方案1:显式禁用B到C的级联持久化(最常用)
@ManyToOne默认不带级联属性,你可以直接显式声明所有操作都不级联到C,仅保留关联查询能力,修改后B类代码如下:Class B{ // cascade设为空即所有操作都不级联到C,按需可加LAZY加载减少不必要查询 @ManyToOne(cascade = {}, fetch = FetchType.LAZY) @JoinColumn(name="cId") private C c; }注意赋值B的C属性时,要给C设置已存在的主键ID,不要直接赋值无ID的空C对象。
方案2:标记C为只读实体(适合全表完全不需要修改的场景)
如果C对应的整张表都是静态只读表,可以直接给C类加只读注解,JPA会直接忽略所有针对C的增删改操作,就算触发级联也不会报错,代码如下:@Entity @Immutable // Hibernate提供的只读注解,标记后该实体不会生成任何增删改SQL @Table(name = "c_table_name") public class C { // 原有属性、get/set方法保持不变 }方案3:业务层提前查询C的托管态对象(无需修改实体配置)
如果你不想调整实体注解,在给B设置C属性前,先通过DAO层从数据库查询出C的持久化对象再赋值,此时JPA会识别到C已经是库中存在的托管对象,不会触发写入操作:// 示例业务代码逻辑 C managedC = cRepository.findById(cId).orElseThrow(() -> new RuntimeException("静态数据不存在")); b.setC(managedC); a.getB().add(b); aRepository.save(a);
额外注意事项
你提供的A类代码存在两处可导致运行异常的错误:
@OneToMany(cascade.=Cascade.ALL中多了多余的小数点,需修正为cascade = Cascade.ALLmappedBy="id"配置错误,mappedBy的值应为B类中关联A的属性名,而非B的主键id,否则会出现关联关系映射异常。
内容的提问来源于stack exchange,提问作者Inder Singh Shekhawat
相关产品推荐
相关产品推荐

