为啥我需要在 Hibernate 中进行事务以进行只读操作?
Posted
技术标签:
【中文标题】为啥我需要在 Hibernate 中进行事务以进行只读操作?【英文标题】:Why do I need Transaction in Hibernate for read-only operations?为什么我需要在 Hibernate 中进行事务以进行只读操作? 【发布时间】:2012-11-12 09:59:54 【问题描述】:为什么我需要在 Hibernate 中进行 Transaction 才能进行只读操作?
以下事务是否在数据库中加了锁?
从数据库中获取的示例代码:
Transaction tx = HibernateUtil.getCurrentSession().beginTransaction(); // why begin transaction?
//readonly operation here
tx.commit() // why tx.commit? I don't want to write anything
我可以使用session.close()
代替tx.commit()
吗?
【问题讨论】:
事务是数据库本身需要的。您可以在此处阅读有关自动提交模式的信息:community.jboss.org/wiki/… @BheshGurung 我想我们只需要为写操作进行事务处理 您是否阅读了链接中的“揭穿自动提交神话”部分? 【参考方案1】:读取事务可能看起来确实很奇怪,在这种情况下人们通常不会标记事务的方法。但是 JDBC 无论如何都会创建事务,如果没有明确设置不同的选项,它将在 autocommit=true
中工作。但是将事务标记为只读是有实际原因的:
对数据库的影响
-
只读标志可以让 DBMS 优化此类事务或并行运行的事务。
拥有跨越多个 SELECT 语句的事务可确保从可重复读取或快照开始的级别的正确隔离(例如,请参阅 PostgreSQL's Repeatable Read)。否则,如果另一个事务并行提交,则 2 个 SELECT 语句可能会看到不一致的画面。这与使用已提交读取无关。
对 ORM 的影响
-
如果您不明确地开始/结束事务,ORM 可能会导致不可预知的结果。例如。 Hibernate 将在第一条语句之前打开事务,但它不会完成它。因此,连接将返回到连接池,并带有未完成的事务。那会发生什么? JDBC 保持沉默,因此这是特定于实现的:mysql、PostgreSQL 驱动程序回滚此类事务,Oracle 提交它。请注意,这也可以在连接池级别进行配置,例如C3P0 为您提供了这样一个选项,默认回滚。
Spring 在只读事务的情况下设置 FlushMode=MANUAL,这会导致其他优化,例如不需要脏检查。根据您加载的对象数量,这可能会带来巨大的性能提升。
对架构和简洁代码的影响
无法保证您的方法不会写入数据库。如果您将方法标记为@Transactional(readonly=true)
,您将指定是否实际上可以在此事务范围内写入数据库。如果您的架构很繁琐,并且某些团队成员可能会选择将修改查询放在预期之外的地方,则此标志会将您指向有问题的地方。
【讨论】:
感谢您的回复。我正在使用休眠(本机休眠,而不是 jpa)。但是休眠强制我在执行任何数据库操作之前启动事务。如果我不启动它会抛出错误,说没有活动的 tracscationn .我可以将交易标记为只读吗,如果可以,如何? readonly 标志实际上是为连接设置的,而不是为事务本身设置的,你不能像那样通过 Hibernate 访问它。您需要使用 Spring Transaction 支持并使用注释或基于 XML 的事务配置。通过这样做,Spring 取代了 Hibernate 获得了连接和事务管理的所有权。 解释得很好! Mark Richards 的另一篇文章很好地解释了事务注释中只读标志的缺陷 - ibm.com/developerworks/java/library/j-ts1/index.html。【参考方案2】:所有数据库语句都在物理事务的上下文中执行,即使我们没有明确声明事务边界(例如,BEGIN、COMMIT、ROLLBACK)。
如果您没有明确声明事务边界,则每个语句都必须在单独的事务中执行(autocommit
模式)。这甚至可能导致每个语句打开和关闭一个连接,除非您的环境可以处理每个线程的连接绑定。
将服务声明为@Transactional
将在整个事务期间为您提供一个连接,并且所有语句都将使用该单个隔离连接。这比一开始就不使用显式事务要好得多。
在大型应用程序上,你可能有很多并发请求,降低数据库连接获取请求率肯定会提高你的整体应用程序性能。
JPA 不对读取操作强制执行事务。只有在您忘记启动事务上下文的情况下,写入才会抛出 TransactionRequiredException
。尽管如此,即使对于只读事务,也最好声明事务边界(在 Spring 中 @Transactional
允许您标记只读事务,这具有很大的性能优势)。
【讨论】:
"...那么每个语句都必须在单独的事务中执行"。容器不会自动批处理? 谢谢弗拉德。我读了你的一些文章。它们真的很有用。 这个***.com/q/34797480/179850 似乎与您断言只读事务具有巨大的性能优势有点矛盾。至少,在 hibernate 的上下文中似乎并非如此。 不,它没有。那是关于设置只读,而我的是关于避免自动提交。【参考方案3】:事务确实在数据库上加了锁——好的数据库引擎以一种合理的方式处理并发锁——并且对于只读使用很有用,以确保没有其他事务添加使您的视图不一致的数据.你总是想要一个事务(虽然有时调整隔离级别是合理的,但最好不要一开始就这样做);如果您在事务期间从不写入数据库,那么提交和回滚事务的结果是相同的(而且非常便宜)。
现在,如果您很幸运,并且您对 DB 的查询使得 ORM 始终将它们映射到单个 SQL 查询,那么您可以在没有 显式 事务的情况下摆脱困境,这依赖于 DB 的内置 -在自动提交行为中,但 ORM 是相对复杂的系统,因此依赖这种行为根本不安全,除非您进行更多工作来检查实现的实际作用。编写显式事务边界要容易得多(尤其是如果您可以使用 AOP 或一些类似的 ORM 驱动技术来实现;我想从 Java 7 开始也可以使用 try-with-resources)。
【讨论】:
【参考方案4】:您是否只读取并不重要 - 数据库仍必须跟踪您的结果集,因为其他数据库客户端可能想要写入会更改您的结果集的数据。
我见过杀死大型数据库系统的错误程序,因为它们只是读取数据,但从不提交,从而迫使事务日志增长,因为数据库无法在 COMMIT 或 ROLLBACK 之前释放事务数据,即使客户几个小时什么都没做。
【讨论】:
以上是关于为啥我需要在 Hibernate 中进行事务以进行只读操作?的主要内容,如果未能解决你的问题,请参考以下文章