JPA保存带可空@ManyToOne关联的Proposta实体时报cascade PERSIST错误
问题描述
使用JPA持久化Proposta实体时,该实体与Motivo实体为多对一关联,映射配置如下:
@Entity @Table( schema = "CADMAT", name = "TB_PROP", uniqueConstraints = { @UniqueConstraint(columnNames = {"TIPO", "COD"}) } ) public class Proposta implements Serializable, Identifiable { // ...其他字段 @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.MERGE, optional = true) @JoinColumn( name = "COD_MOTIV", columnDefinition = "NUMERIC", referencedColumnName = "COD", insertable = false, updatable = false, nullable = true) private Motivo motivo; // ...其他方法 }
创建新记录时抛出以下错误:
javax.persistence.RollbackException: java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST: br.gov.sp.sefaz.siafem.cm.db.model.Motivo@4ecd00b5.
业务规则要求Proposta可以不关联Motivo单独保存,因此不能给该关联配置CascadeType.PERSIST;已经配置了optional=true和nullable=true,错误依然存在。
故障原因
这个报错和optional、nullable配置无关,这两个参数仅控制关联字段是否允许存空值,不会干预关联对象的持久化校验逻辑。
报错的核心触发条件是:执行持久化操作时,Proposta实例的motivo属性不是null,而是指向了一个刚创建、未持久化、不在当前持久化上下文管辖范围内的Motivo实例;由于关联没有配置CascadeType.PERSIST,JPA在同步持久化上下文、遍历对象图时发现了这个不受管控的新对象,就会直接抛出异常。
你给关联字段加了insertable = false, updatable = false配置,只是告诉JPA不要把这个字段的值拼接到insert/update SQL里,但JPA依然会在持久化阶段校验对象图上所有关联对象的状态,这个校验逻辑不受insertable/updatable参数影响。
修复方案
结合你的业务要求,不需要修改级联配置,按实际场景调整赋值逻辑即可:
- 保存
Proposta时不需要关联Motivo:调用persist方法前,手动将motivo字段设为null,不要传入new出来的空Motivo实例占位。 - 保存
Proposta时需要关联已有的Motivo:不要传入新创建的Motivo对象,先通过主键查询到数据库中已存在、处于托管状态的Motivo实例,再set到Proposta的motivo属性后执行保存。 - 如果后续业务变更,需要在保存
Proposta时同步新建关联的Motivo,再给关联加上CascadeType.PERSIST即可,当前业务不支持该场景可以忽略。
不要用未持久化的新建实体给关联字段占位,不需要关联就设为null,需要关联就传入已持久化的托管对象,这个错误就不会出现。
内容的提问来源于stack exchange,提问作者Gustavo de Freitas
相关产品推荐
相关产品推荐

