JPA保存记录后自增字段值未在实体对象中回显的问题排查
问题描述
使用JPA保存实体后,数据库中已成功插入记录且自增字段seqId生成了对应值,但通过原实体对象调用mytable.getSeqId()时返回null。
相关代码
实体类
@Entity(name = "my_table") public class MyTable { @Id @Column(name = "request_id", nullable = false) private String requestId; @GeneratedValue(strategy = GenerationType.IDENTITY) // 自增字段 @Column(name = "seq_id") private Long seqId; @Column(name = "customer_id", nullable = false) private String customerId; // 省略getter、setter方法 }
JPA Repository
@Repository public interface MyTableRepo extends JpaRepository<MyTable, String> { Optional<MyTable> findById(final String requestId); }
服务类
@Service @RequiredArgsConstructor public class MyTableService { private final MyTableRepo myTableRepo; @Transactional(propagation = Propagation.REQUIRES_NEW) public MyTable save(final MyTable myTable){ return myTableRepo.save(myTable); } }
调用代码
MyTable mytable = new MyTable(); mytable.setRequestId("someValue"); mytable.setCustomerId("123241"); myTableService.save(mytable); mytable.getSeqId(); // 此处返回null
原因分析
- 事务隔离导致实体游离:
save方法使用Propagation.REQUIRES_NEW会开启独立新事务,事务提交后,原实体对象脱离当前持久化上下文的管理,变为游离状态,JPA不会自动将数据库生成的seqId同步回原对象。 - save方法返回新的托管实体:JPA的
save方法会返回一个处于托管状态的实体对象,该对象包含数据库生成的最新值,但原传入的对象不会被修改。
解决方法
方法1:使用save方法返回的实体获取值
这是最直接且推荐的方案,接收save方法返回的实体,从该对象中读取自增字段:
MyTable mytable = new MyTable(); mytable.setRequestId("someValue"); mytable.setCustomerId("123241"); MyTable savedTable = myTableService.save(mytable); Long seqId = savedTable.getSeqId(); // 可获取正确的自增值
方法2:调整事务传播行为(业务允许时)
如果不需要强制开启新事务,将事务传播级别改为默认的Propagation.REQUIRED,这样原实体对象会在同一个持久化上下文中,事务提交后JPA会自动同步自增字段到原对象:
@Service @RequiredArgsConstructor public class MyTableService { private final MyTableRepo myTableRepo; @Transactional // 默认使用Propagation.REQUIRED public MyTable save(final MyTable myTable){ return myTableRepo.save(myTable); } }
此时原调用代码中mytable.getSeqId()即可获取到正确值。
方法3:手动刷新持久化上下文(特殊场景用)
若必须保留REQUIRES_NEW事务,可在save方法中手动刷新上下文,强制同步数据库状态到实体,但会增加性能开销,仅在必要时使用:
@Service @RequiredArgsConstructor public class MyTableService { private final MyTableRepo myTableRepo; private final EntityManager entityManager; @Transactional(propagation = Propagation.REQUIRES_NEW) public MyTable save(final MyTable myTable){ MyTable saved = myTableRepo.save(myTable); entityManager.flush(); // 强制将实体状态同步到数据库 entityManager.refresh(saved); // 从数据库刷新实体的最新状态 return saved; } }
同样需要通过返回的saved对象获取seqId。
内容的提问来源于stack exchange,提问作者Arun Prabhath
相关产品推荐
相关产品推荐

