调用JPA TypedQuery.getResultList()是否会导致事务提交?
问题说明
运行环境为Java 1.8 SE,使用Hibernate 5作为JPA提供者,测试代码如下:
EntityManagerFactory emf = Persistence.createEntityManagerFactory("EmployeeService"); EntityManager em = emf.createEntityManager(); em.getTransaction().begin(); Employee employee = new Employee(1); employee.setName("Bob"); employee.setSalary(100000); em.persist(employee); System.out.println("em" + em.find(Employee.class, 1)); System.out.println("em all Employees" + em.createQuery("SELECT e from Employee e", Employee.class).getResultList()); em.clear(); em.getTransaction().commit(); System.out.println("em after commit " + em.find(Employee.class, 1)); System.out.println("em after commit all Employees" + em.createQuery("SELECT e from Employee e", Employee.class).getResultList()); em.close(); emf.close();
原逻辑预判:事务提交前调用em.clear()会清空持久化上下文,未刷入数据库的持久化实体不会被提交,预期输出如下:
emEmployee(id=1, name=Bob, salary=100000.0) em after commit null em after commit all Employees[]
实际运行输出为:
emEmployee(id=1, name=Bob, salary=100000.0) em all Employees[Employee(id=1, name=Bob, salary=100000.0)] em after commit Employee(id=1, name=Bob, salary=100000.0) em after commit all Employees[Employee(id=1, name=Bob, salary=100000.0)]
如果注释掉em.clear()前的getResultList()调用行,就能得到预期输出。疑问点为:为什么调用getResultList()后无法得到预期结果,该方法是否会导致事务提前提交?
问题解答
getResultList() 不会导致事务提前提交,这个现象是JPA的自动flush机制导致的,具体逻辑如下:
- Hibernate默认的
FlushMode为AUTO,这是JPA规范定义的默认行为:当执行会被持久化上下文未提交变更影响的JPQL/Criteria查询时,为了保证查询结果和内存中实体状态一致,Hibernate会先自动触发一次flush操作,把当前持久化上下文中所有未同步到数据库的变更(也就是本例中刚persist的Employee实体对应的INSERT语句)发送到数据库端的事务缓冲区,这个操作只是同步SQL到数据库,不会执行事务提交。 - 后续调用的
em.clear()只会清空当前EntityManager的一级缓存(即持久化上下文里托管的实体引用),但已经发送到数据库事务缓冲区的SQL不会被撤销。等到调用transaction.commit()时,数据库会直接把这部分已经存在的变更提交入库,所以提交后依然能查到对应记录。 - 注释掉
getResultList()后符合预期的原因:em.find()是主键查询,会优先读取一级缓存中的实体,不会触发自动flush。此时persist()的实体仅存在于持久化上下文中,没有同步任何SQL到数据库,调用clear()会直接把这个托管实体从上下文移除,后续commit()时没有需要提交的变更,自然查不到对应记录。
内容的提问来源于stack exchange,提问作者James
相关产品推荐
相关产品推荐

