tnxRepository继承JpaRepository仅存一个实体却触发双实体保存?CrudRepository无此问题
为什么JpaRepository调用save会保存两个实体,而CrudRepository只保存目标实体?
这事儿本质和JpaRepository/CrudRepository本身的save方法关系不大,核心原因在于实体是否处于EntityManager的托管状态,以及代码运行时的事务范围。
先理清两个关键概念
- 托管状态实体:当你在一个活跃的事务中通过
findById查询实体时,这个实体会被JPA的核心管理类EntityManager接管。只要事务没结束,EntityManager会实时跟踪实体的属性变化——哪怕你不调用save,事务提交时它都会自动把更新同步到数据库。 - 脱管状态实体:如果查询操作不在事务中(或者事务已经结束),返回的实体就脱离了EntityManager的管控。你修改它的属性后,必须显式调用
save才能把变化写到数据库。
针对你的场景拆解分析
用JpaRepository时两个实体都被保存的原因
大概率你的这段代码运行在一个完整的事务上下文中(比如所在的Service方法加了@Transactional注解):
- 调用
tnxRepository.findById(id)和findById(2)时,因为事务处于活跃状态,返回的tnx和tnx2都是托管状态。 - 你修改两个实体的
txt属性后,EntityManager会默默记录下这些变更。 - 调用
save(tnx)时,由于tnx已经是托管状态,JPA的merge操作只是确认实体状态,没有额外的写入动作。 - 当事务最终提交时,EntityManager会执行「脏检查」——发现
tnx和tnx2的属性都被修改了,就把两个实体的更新同步到数据库,所以你看到两个都被保存了。
用CrudRepository时只保存目标实体的原因
这种情况通常是代码不在同一个事务中导致的:
- 调用
findById(id)时,CrudRepository的findById方法默认是只读事务,查询完成后事务就结束了,返回的tnx变成脱管状态。 - 修改
tnx.txt后,因为实体已经脱管,EntityManager不会跟踪这个变化。 - 同理,
findById(2)返回的tnx2也是脱管状态,修改后也不会被自动跟踪。 - 调用
save(tnx)时,会开启一个新事务,把tnx的修改合并到数据库;但tnx2是脱管状态,你没有显式调用save(tnx2),所以它的修改不会被保存。
补充说明
其实JpaRepository和CrudRepository的save方法底层都是SimpleJpaRepository实现的,逻辑完全一致——差异根本不在接口本身,而在于调用方法时的事务上下文。如果把使用CrudRepository的代码也放到同一个事务里,你会发现两个实体同样都会被自动保存。
内容的提问来源于stack exchange,提问作者Rockman12352
相关产品推荐
相关产品推荐

