ChatGPT解决这个技术问题 Extra ChatGPT

为什么我需要在 Hibernate 中进行事务以进行只读操作?

为什么我需要在 Hibernate 中进行事务以进行只读操作?

以下事务是否在数据库中加了锁?

从数据库中获取的示例代码:

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 我想我们只需要为写操作进行事务处理
您是否阅读了链接中的“揭穿自动提交神话”部分?

S
Stanislav Bashkyrtsev

用于读取的事务可能看起来确实很奇怪,并且在这种情况下人们通常不会标记事务的方法。但是 JDBC 无论如何都会创建事务,如果没有明确设置不同的选项,它只会在 autocommit=true 中工作。但是将事务标记为只读是有实际原因的:

对数据库的影响

只读标志可以让 DBMS 优化此类事务或并行运行的事务。拥有跨越多个 SELECT 语句的事务可以保证从可重复读取或快照开始的级别的正确隔离(例如,请参阅 PostgreSQL 的可重复读取)。否则,如果另一个事务并行提交,则 2 个 SELECT 语句可能会看到不一致的画面。使用已提交读时,这无关紧要。

对 ORM 的影响

如果您没有明确地开始/结束事务,ORM 可能会导致不可预知的结果。例如,Hibernate 将在第一条语句之前打开事务,但它不会完成它。因此,连接将返回到连接池,并带有未完成的事务。那会发生什么? JDBC 保持沉默,因此这是特定于实现的:MySQL、PostgreSQL 驱动程序回滚此类事务,Oracle 提交它。请注意,这也可以在连接池级别进行配置,例如 C3P0 为您提供了这样一个选项,默认情况下回滚。 Spring 在只读事务的情况下设置 FlushMode=MANUAL,这会导致其他优化,例如不需要脏检查。这可能会导致巨大的性能提升,具体取决于您加载的对象数量。

对架构和干净代码的影响

不能保证您的方法不会写入数据库。如果您将方法标记为 @Transactional(readonly=true),您将指定是否实际上可以在此事务的范围内写入 DB。如果您的架构很繁琐,并且某些团队成员可能会选择将修改查询放在不期望的地方,则此标志会将您指向有问题的地方。


感谢您的回复。我正在使用休眠(本机休眠,而不是 jpa)。但是休眠迫使我在执行任何数据库操作之前启动一个事务。如果我不启动一个它会抛出错误,说没有活动的 tracscationn。我可以将事务标记为只读,如果是的话怎么办?
readonly 标志实际上是为连接设置的,而不是为事务本身设置的,并且您不能像那样通过 Hibernate 访问它。您需要使用 Spring Transaction 支持并使用注释或基于 XML 的事务配置。通过这样做,Spring 获得了连接和事务管理的所有权,而不是 Hibernate。
很好解释! Mark Richards 的另一篇文章很好地解释了事务注释中只读标志的缺陷 - ibm.com/developerworks/java/library/j-ts1/index.html
V
Vlad Mihalcea

所有数据库语句都在物理事务的上下文中执行,即使我们没有明确声明事务边界(例如,BEGIN、COMMIT、ROLLBACK)。

如果您没有明确声明事务边界,则每个语句都必须在单独的事务中执行(autocommit 模式)。这甚至可能导致每个语句打开和关闭一个连接,除非您的环境可以处理每个线程的连接绑定。

将服务声明为 @Transactional 将在整个事务期间为您提供一个连接,并且所有语句都将使用该单个隔离连接。这比首先不使用显式事务要好得多。

在大型应用程序上,您可能有很多并发请求,降低数据库连接获取请求率肯定会提高您的整体应用程序性能。

JPA 不对读取操作强制执行事务。只有在您忘记启动事务上下文的情况下,写入才会抛出 TransactionRequiredException。尽管如此,即使对于只读事务也最好声明事务边界(在 Spring @Transactional 中允许您标记只读事务,这具有很大的性能优势)。


“......那么每个语句都必须在单独的事务中执行”。容器不会自动进行批处理?
谢谢弗拉德。我读了你的一些文章。它们真的很有用。
这个 stackoverflow.com/q/34797480/179850 似乎与您断言只读事务具有巨大的性能优势有点矛盾。至少,在休眠的情况下似乎并非如此。
不,它没有。那是关于设置只读的,而我的是关于避免自动提交的。
H
Hearen

事务确实在数据库上加了锁——好的数据库引擎以合理的方式处理并发锁——并且对于只读使用很有用,以确保没有其他事务添加使您的视图不一致的数据。你总是想要一个事务(虽然有时调整隔离级别是合理的,但最好不要一开始就这样做);如果您在事务期间从不写入数据库,那么提交和回滚事务的结果是相同的(而且非常便宜)。

现在,如果你很幸运,并且你对 DB 的查询使得 ORM 总是将它们映射到单个 SQL 查询,那么你可以在没有显式事务的情况下逃脱,依赖于 DB 的内置自动提交行为,但 ORM 是相对复杂的系统因此,除非您进行更多工作来检查实现的实际作用,否则依赖这种行为是完全不安全的。在其中编写显式事务边界要容易得多(尤其是如果您可以使用 AOP 或一些类似的 ORM 驱动技术来做到这一点;我想从 Java 7 开始也可以使用 try-with-resources)。


I
Ingo

无论您是否只读取都没有关系 - 数据库仍必须跟踪您的结果集,因为其他数据库客户端可能想要写入会更改您的结果集的数据。

我见过杀死大型数据库系统的错误程序,因为它们只是读取数据,但从不提交,从而迫使事务日志增长,因为数据库无法在 COMMIT 或 ROLLBACK 之前释放事务数据,即使客户端什么也没做用了几个小时。