【问题标题】:Why set Autocommit to true?为什么将 Autocommit 设置为 true?
【发布时间】:2011-05-26 02:58:16
【问题描述】:

我一直想知道为什么 JDBC API 提供自动提交模式 (java.sql.Connection.setAutocommit())。这似乎是一种有吸引力的滋扰,只会引诱人们陷入困境。我的理论是,它只是被添加到 JDBC 中,以便为想要创建使用 JDBC 编辑和运行 SQL 的工具的供应商简化生活。是否有任何其他原因打开自动提交,或者它总是一个错误?

【问题讨论】:

    标签: java jdbc


    【解决方案1】:

    在全局级别启用“自动提交”时,需要仔细查看某些条件:

    a.) 查询级别的事务管理将留给用户,例如,如果需要一堆查询一起成功或失败,则需要将其包装在 BEGIN 下并提交事务。

    b.) 请记住,启用“自动提交”时不会回滚。

    c.) 编写(提交)每个事务也会产生开销。

    d.) 对于只读查询,没有明确需要“自动提交”,但通过启用“自动提交”,它会自动对所有查询强制执行。

    如果表锁定是启用自动提交的唯一问题,那么这可能不是一个好主意,而是可以求助于较低的锁定超时。

    【讨论】:

      【解决方案2】:

      自动提交很方便;但随着对 JDBC 3 规范的更改,它变得不那么有用了。

      由于“自动提交”模式下的 JDBC 3 连接不能打开多个语句。执行另一个语句,将关闭第一个语句——包括任何 ResultSet。

      因此,在 SELECT 中循环并发出 UPDATE(甚至是嵌套的 SELECT)往往会失败。显然这是一种犯罪行为,实际上想要做某事用你的外部 SELECT 的结果!


      无论如何,取决于特定的驱动程序和版本。但总的来说,JDBC 3 规范似乎强制执行这种无益的行为。升级驱动程序也可能无益地“发现”这种行为。

      为什么要使用自动提交?最初,它很有帮助且方便。正如其他答案所说,JDBC 需要大量的 GUFF 和 HANDLING 才能正确调用.. JDBC 并不是一个设计良好的 API :(


      现在,您最好使用 Hibernate 或 Spring 的 JdbcTemplate。如果您正在做 servlet/Web 应用程序,请将您的事务管理(开始/结束)或 Hibernate 会话(将其绑定到线程本地)在“用户请求”的边界。

      例如,在 ServletRequest 开始时绑定您的连接/事务;并在最后归还。

      您可以使用 javax.servlet.Filter 或类似的,并将其绑定到线程本地,制作静态助手来获取它或 require 它等等。

      【讨论】:

      • 请不要推荐绑定到网络请求反模式的连接/事务/会话...如果您希望您的事务跨越例如请求处理、multiplart 上传处理、将正文发送到连接速度较慢的客户端您可以这样做,但我认为在几乎所有情况下都是非常糟糕的主意。
      • 谢谢@PiotrMüller——有什么推理或参考吗?我可能希望您可以为几乎任何模式提出 0.001% 的坏情况,但最好将“孤立的有问题的极端情况”与“几乎所有情况”区分开来——当时我给出了这个建议(2012),我相信这是个好建议。用户请求边界是 HTTP 服务应用程序中最基本和最重要的边界。正因为如此,它是一个强大而明显的设计候选者。任何替代解决方案都可能更复杂。
      • 在网络应用程序中,大多数提到的“坏情况”都将适用(对于任何典型的网络应用程序)。启用会话时,您应该确保立即解决许多技术方面的问题,例如响应缓冲:确保事务和 SQL 连接在客户端连接拉取响应的整个过程中不跨越(如果客户端带宽低,为什么要长时间保持 SQL 连接)。如果应用程序,这也很快会导致设计/性能问题生长。对于一个快速的、几周的小项目,它可能是一些解决方案,但我不推荐它用于任何大型项目
      • 随着应用程序的增长/可靠性要求的提高,这些都是需要注意的好因素,感谢@PiotrMüller。
      【解决方案3】:

      不幸的是,使用自动提交是特定于数据库的(就像事务行为一样)。我认为如果你没有一个全局的、程序化的事务策略,自动提交可能比仅仅希望每个人都正确地关闭/回滚事务更好。

      对于 MySQL,您可以默认打开 autocommit=true,当您开始事务时它会自动关闭它。设置 autocommit=false 的唯一原因是,如果有人尝试在没有 BEGIN 的情况下启动事务,您想强制出错。

      为了简化当今典型的 Java + MySQL 应用程序,我或多或少会忽略自动提交设置,使用 open-session-in-view 模式并称之为好。

      我强烈反对显式的 RDBMS 行锁,而是使用乐观锁。 Hibernate 提供了对乐观锁的内置支持,但即使是手动代码也很容易采用这种模式并提供更好的性能。

      【讨论】:

      • 为了阐明典型的 Java + MySQL 应用程序:将 auto-commit 设置为 true。在传入请求(网页或 REST/JSON)开始时打开一次会话(并开始事务)。如果请求处理正常,则在请求处理结束时提交事务。然后,为请求添加回滚到您的 500 页呈现。例如,这基本上就是 Play Framework 处理事情的方式。它对用户和开发者来说都很直观。
      • 这是一个很好的答案——“网络请求”应该与网络应用程序中的“数据库事务”密切相关。因此我们需要依赖乐观,而不是数据库锁定。它有助于使用 OpenSessionInView 在整个请求中保持会话打开,包括视图呈现,然后在请求结束时刷新/提交。
      • 在我看来,这不是一个好的答案,如果典型的 Java+MySQL 应用程序不仅仅是“几个星期的项目”,请考虑将 session-opened-in-view 作为反模式。您将很快失去对应用程序中发生的事情的控制,它实际上肯定会导致性能或并发问题,自动提交将使您没有事务超时或事务超时作为“一般请求处理超时”工作。我会说,在超过 90% 的情况下,Web 请求 NO 与 SQL 事务相关
      • 没有提及您可能遇到的其他问题,例如产生响应时,sql 事务超时可以与客户端网络速度相结合,这取决于许多其他设置,例如缓冲。请不要推荐 jpa-session-in-view ANTI-pattern。
      【解决方案4】:

      我几乎总是使用 autocommit=true 运行。 99% 的时间,我的更新都是原子的。当然,在某些情况下,如果您写入借方,然后尝试写入您想要回滚的贷方失败。但根据我的经验,这些比较少见。通常我写的每条记录都是独立的。在这种情况下,每次写入后无需费心进行提交是很方便的。它在这里和那里节省了一行代码。如果考虑到程序的结构,它可能会节省更多,这意味着我不需要额外的 try/catch 块,或者我不需要在函数之间传递连接对象。它可以避免有人忘记提交的烦人错误。

      我看到它可能“引诱某人陷入麻烦”的唯一方法是他认为关闭自动提交并执行提交或回滚太麻烦了,因此他执行了应该单独在事务中的更新。然后,只要没有发生任何应该中止事务的事情,一切似乎都可以正常工作。如果测试场景不足,我可以想象这会进入生产环境。

      但是你几乎可以对语言的任何特性说同样的话。比如,假设你编写了一个程序,它处理的数字在 90% 的时间内都适合,但有时可能会更大。面对这种情况,正确的做法是使用 BigInteger 或创建一个新类来处理更大的数字。一个懒惰的程序员可能会被引诱使用 long,因为它通常会起作用,而其他替代方案太麻烦了。因此,您是否会得出结论认为 Java 不应该包含 long(或 int),因为有人可能会在它们不合适时被引诱使用它们?

      如果在您的程序中大部分更新必须在事务上下文中完成,则关闭自动提交。它的存在不会伤害你。它在方便时就在那里,但在不方便时,您可以将其关闭。

      【讨论】:

      • 自 JDBC 3 规范以来,“自动提交”模式下的连接不能打开多个语句。因此,在外部 SELECT 循环中嵌套的 UPDATE 甚至 SELECT 都会导致问题。
      • 这不仅是原子性,如果事务管理器支持,事务也可以分配超时。使用自动提交和无查询超时可能会使您陷入未检测到的 SQL 性能问题、挂起的繁重查询等。
      • 我已经离开 Javaland 很多年了,所以可能新版本的 Java 有所不同,或者我只是记错了。但我很确定在使用自动提交执行查询时我得到了 SQL 超时。我对在开发环境中运行 find 但在 prod 中超时的查询有很多美好的回忆。也许默认值是不同的或什么的。无论如何,依靠超时来告诉您有性能问题是一个非常生硬的工具。
      【解决方案5】:

      我现在使用的 95% 的代码库都涉及单个更新,其中自动提交是完全合理的。所以,我们默认开启它。只需将其关闭足够长的时间以执行需要成为事务的代码部分,然后自动提交就会重新开启!

      【讨论】:

      • 请记住,交易也可以让您的交易管理器为您提供交易超时。您是否为每个查询设置了超时时间?
      【解决方案6】:

      我能看到的唯一合理原因是在小型应用程序的简单单查询事务中摆脱 connection.commit()connection.rollback() 样板。原始形式的 JDBC 本身就需要很多样板文件。每行少一点,JDBC 对初学者来说就不那么可怕了。

      【讨论】:

        【解决方案7】:

        提交模式改变了数据库持有锁的方式。

        建议仅在事务模式期间禁用自动提交模式。这样,您可以避免为多个语句持有数据库锁,从而增加与其他用户发生冲突的可能性。

        ...

        为避免事务期间发生冲突,DBMS 使用锁,这是一种阻止其他人访问事务正在访问的数据的机制。 (请注意,在自动提交模式下,每条语句都是一个事务,只为一个语句持有锁。)

        http://download.oracle.com/javase/tutorial/jdbc/basics/transactions.html

        【讨论】:

        • 考虑到如今即使是 mySQL 和 DB2 也是多版本的,该教程变得有些过时了。 :(
        • 快照隔离级别不是免费提供的:blogs.msdn.com/b/sqlserverstorageengine/archive/2008/03/30/… 您可以通过快照隔离获得并发性,但会增加开销。
        • 了解获取锁的方式总是很重要的。
        • 当然,我只是认为教程的​​实际语气是“您应该使用自动提交来防止数据库锁争用”。是虚伪的。这是一个过于简单的笼统说法,即 2010 年可能已经成为少数用例。 (但作为对所提出的实际问题的回答,确实可以很好地了解为什么(老化)API 是这样设计的!)
        • 自动提交已经很老了。我认为答案比说“好吧,你不必输入 commit ...”之类的内容更能深入了解它存在的背景。
        猜你喜欢
        • 1970-01-01
        • 2019-01-06
        • 2017-10-31
        • 1970-01-01
        • 1970-01-01
        • 2015-02-08
        • 2010-11-10
        • 2023-03-09
        • 2014-11-05
        相关资源
        最近更新 更多