我需要一些帮助来解决Hibernate TransientPropertyValueException
我们有两个实体:
@Entity
@Table(name = "TABLE_A")
public class TableA {
@Id
@Column(name = "EXT_ID", nullable = false, unique = true, updatable = false)
private String extId;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "EXT_ID", updatable = false, insertable = false)
private TableB tableB;
(...)
}
@Entity
@Table(name = "TABLE_B")
public class TableB {
@Id
@Column(name = "EXT_ID", nullable = false)
private String extId;
@OneToOne
@JoinColumn(name = "EXT_ID")
private TableA tableA;
}
现在我们使用TableA
删除CrudRepository
:
@Override
@Transactional
public boolean cancel(String extId) {
Optional<TableA> maybeTableA = repository.findTableA(extId, DELETE_STATUS_SET);
return maybeTableA.map(tableA -> {
repository.delete(tableA);
return true;
}).orElse(false);
}
到目前为止一切正常。但是现在每当我想查询引用TableB
TableA
的{{1}}时,我都会TransientPropertyValueException
很明显,在提交交易之前,TableB
到TableA
的引用尚未删除。
有没有一种很好的方法来解决这个问题?
我在删除后直接尝试了sessionFactory.getCurrentSession().flush()
,但它也无法正常工作
唯一有效的选择是手动删除事务中的引用:
tableB.setTableA(null);
tableBRepository.save(tableB);
但这有点像hacky imo
答案 0 :(得分:1)
当谈到JPA关系时,你有责任更新关系的双方。
此处,cancel
方法应更新tableA
和tableB
。否则,你得到的是tableB中的EXT_ ID
值,它在tableA中没有对应关系。然后,在检索tableB实体时,Hibernate需要一个相应的tableA实体,它不再存在并抛出错误。
如果tableB实体在删除其对应的tableA实体时应保留,则表{B} extId
需要可以为空。
还有一件事:与TableA
一样,TableB
必须将extId
或tableA
声明为updatable = false, insertable = false
。 Hibernate不喜欢同一个数据库字段上的两个可写属性。
@Override
@Transactional
public boolean cancel(String extId) {
Optional<TableA> maybeTableA = repository.findTableA(extId, DELETE_STATUS_SET);
return maybeTableA.map(tableA -> {
// update tableB
TableB tableB = tableA.getTableB();
tableB.setExtId(null);
tableB.setTableA(null);
tableBRepository.save(tableB);
// and update tableA
repository.delete(tableA);
return true;
}).orElse(false);
}
答案 1 :(得分:0)
您是否尝试过@MapsId注释?
https://vladmihalcea.com/the-best-way-to-map-a-onetoone-relationship-with-jpa-and-hibernate/
答案 2 :(得分:0)
你看过级联吗?我自己会选择你的hacky方法,因为我发现它更容易阅读并遵循代码。