Hibernate无事务查询select方法实现正确性及优化方案咨询
现有无事务方法的合理性评估
你当前写的withoutTransation实现在单查询、要求不高的简单只读场景下可以跑通,但并不是最优方案,存在风险和性能优化空间,具体问题如下:
- 有意外写入风险:如果
invocation.invoke逻辑中不小心修改了查询得到的托管实体属性,EntityManager在关闭前若触发隐式flush,会将修改直接同步到数据库,违背只读场景的设计初衷。 - 无法保证读一致性:如果
invocation内执行了多次关联查询,没有事务边界的情况下,多次查询之间如果有其他写入事务提交,会出现不可重复读、幻读等问题,得不到一致的快照结果。 - 性能未必更优:大多数数据库和JDBC驱动默认会为每个查询自动创建隐式的自动提交事务,多次查询会产生多个事务开销,反而不如显式开一个只读事务效率高。
更优的实现方案
推荐优先选择显式只读事务方案,既规避上面的问题,还能获得性能优化收益,示例实现如下:
public static <T> Response<T> readOnlyTransaction(JPA jpa, String jpaPersistence, Logger logger, Invocation<T> invocation) { EntityManager em = null; EntityTransaction tx; Response<T> result; try { em = jpa.getEntityManager(jpaPersistence); // 禁止任何flush操作,彻底避免意外写入 em.setFlushMode(FlushModeType.NEVER); tx = em.getTransaction(); try { tx.begin(); // 标记事务只读,驱动和数据库会自动优化锁、日志策略 tx.setReadOnly(true); result = invocation.invoke(em); tx.commit(); return result; } finally { if (tx.isActive()) { tx.rollback(); } } } catch (PersistenceException | ResourceNotFoundException e) { logger.error("Exception in read only method invocation", e); return new Response<T>(Status.SERVER_ERROR); } finally { if (em != null && em.isOpen()) { em.close(); } } }
如果你的场景确实不需要多次查询的一致性,坚持要使用无事务实现,也建议在获取EntityManager后立刻设置em.setFlushMode(FlushModeType.NEVER),规避意外写入的风险,再执行查询逻辑。
内容的提问来源于stack exchange,提问作者Radek
相关产品推荐
相关产品推荐

