如何在 JDBC 中启动事务?
Posted
技术标签:
【中文标题】如何在 JDBC 中启动事务?【英文标题】:How to start a transaction in JDBC? 【发布时间】:2011-06-23 20:41:53 【问题描述】:Connection.setTransactionIsolation(int)
警告:
注意:如果在事务期间调用此方法,则结果是实现定义的。
这就引出了一个问题:如何在 JDBC 中开始事务?很清楚如何结束事务,但不清楚如何开始。
如果Connection
在事务内部开始,我们应该如何在事务外部调用Connection.setTransactionIsolation(int)
以避免特定于实现的行为?
【问题讨论】:
来自 JDBC 4.0 规范:“何时开始新事务是由 JDBC 驱动程序或底层数据源隐式做出的决定。虽然一些数据源实现了显式的“开始事务”语句,但有" 【参考方案1】:回答我自己的问题:
JDBC connections 开始时启用自动提交模式,其中每个 SQL 语句都隐式地与一个事务划界。 希望每个事务执行多个语句的用户必须转至auto-commit off。 更改自动提交模式会触发当前事务的提交(如果一个处于活动状态)。 如果启用了自动提交,则可以随时调用Connection.setTransactionIsolation()
。
如果禁用自动提交,Connection.setTransactionIsolation()
只能在事务之前或之后调用。在事务中间调用它会导致未定义的行为。
请参阅 Oracle 的 JDBC Tutorial。
【讨论】:
【参考方案2】:将一个连接用于多个事务(重用、池化或链接),一些奇怪的问题可能会潜伏产生人们不得不忍受的问题,因为他们通常无法确定原因。
想到以下场景:
-
(重新)使用正在进行/未提交事务的连接
有缺陷的连接池实现
在某些数据库(尤其是分布式 SQL 和 NoSQL 数据库)中实现更高的隔离级别
第 1 点直截了当且易于理解。 第 2 点基本上通向第 1 点或(和)第 3 点。
第 3 点是关于在发出第一条语句之前已经开始新事务的系统。从数据库的角度来看,这样的事务可能早在发出“第一个”真实语句之前就已经开始了。如果并发模型基于快照概念,即仅读取在事务开始时有效的状态/值,但没有稍后更改的更改,那么在提交当前事务的完整读取集时非常重要也经过验证。
由于 NoSQL 和某些隔离级别(如 MS SQL-Server 快照)通常不会验证读取集(以正确的方式),所有的赌注通常都与预期的结果相符。虽然这是一个始终存在的问题,但当处理在最后一次提交时开始的事务或当连接被池化而不是实际使用的连接时,情况会更糟,确保事务实际开始通常很重要预计何时开始。 (如果使用回滚只读事务也非常重要)。
我在 JAVA 中处理 JDBC 时使用以下规则:
-
如果公司将普通 JDBC 与任何池机制结合使用,则始终在使用 JDBC 连接之前回滚(废弃所有数据并启动新事务)
使用 Hibernate 进行事务处理,即使只使用会话管理的 JDBC 连接来处理普通 SQL。到目前为止,交易从未出现过问题。
使用 BEGIN / COMMIT / ROLLBACK 作为 SQL 语句(如前所述)。如果您在活动事务期间发出 BEGIN 语句,大多数实现将失败(为您的数据库测试它并记住测试数据库不是生产数据库,并且 JDBC 驱动程序和 JDBC 服务器端实现在行为上可能与在实际服务器)。
在自己的包装器中使用 3 用于 JDBC 连接实例。这种方式的事务处理总是正确的(如果没有使用反射并且连接池没有缺陷)。
3+4 我只在响应时间很关键或 Hibernate 不可用时使用。 4 允许针对特殊情况使用一些更高级的性能(响应时间)改进模式
【讨论】:
【参考方案3】:如果您希望将连接保持在“setAutoCommit(true)”模式但仍需要事务,则可以手动运行事务:
try (Statement statement = conn.createStatement())
statement.execute("BEGIN");
try
// use statement ...
statement.execute("COMMIT");
catch (SQLException failure)
statement.execute("ROLLBACK");
【讨论】:
不需要禁用自动提交? 似乎对我有用......虽然它可能会混淆 JDBC 类! :) 它可能会也可能不会跨数据库移植... :) 如果您在 1 个事务中没有多个语句,则此解决方案很好,否则使用其他方法【参考方案4】:您可以使用这些方法进行交易:
-
你必须创建像
con
这样的连接对象
con.setAutoCommit(false);
您的查询
如果一切都是真的con.commit();
否则con.rollback();
【讨论】:
这并没有真正添加其他答案未涵盖的任何新内容...... 我假设对con.commit();
的调用包装/添加了要在BEGIN TRANSACTION;
和COMMIT;
语句中执行的SQL(或以一种可以被视为等效的方式运行),取消代码需要明确包含这些语句的行。对吗?
实际上,我最喜欢这个答案,因为它很好地概述了“正常流程”,并让我明白了setAutoCommit(false)
为驾驶员设置舞台的观点(至少对我而言)在发送您的下一个 Statement
之前开始新的交易。【参考方案5】:
也许这会回答你的问题: 每个连接只能有一个事务。 如果自动提交开启(默认),每次选择、更新、删除都会自动启动并提交(或回滚)一个事务。 如果您关闭自动提交,您将启动一个“新”事务(意味着提交或回滚不会自动发生)。在某些语句之后,您可以调用 commit 或 rollback,它会完成当前事务并自动启动一个新事务。 您不能在纯 JDBC 上的一个 JDBC 连接上主动打开两个事务。
【讨论】:
【参考方案6】:我建议你阅读this你会看到
因此,第一次调用 setAutoCommit(false) 和每次调用 commit() 隐式标记开始 一笔交易。交易可以 在他们被提交之前撤消 打电话
编辑:
查看 JDBC Transactions 的官方文档
创建连接时,它处于自动提交模式。这意味着 每个单独的 SQL 语句都被视为一个事务,并且是 执行后立即自动提交。 (为了更 准确地说,默认是在提交 SQL 语句时提交 完成,而不是执行时。当所有的语句完成时 其结果集和更新计数已被检索。在几乎 但是,在所有情况下,声明都已完成,因此已提交, 执行后立即执行。)
允许将两个或多个语句组合成一个 事务是禁用自动提交模式。这是演示 在以下代码中,其中 con 是一个活动连接:
con.setAutoCommit(false);
来源:JDBC Transactions
【讨论】:
我在您链接的页面上找不到上述引用。此外,我更喜欢在 JDBC 规范中找到引用,而不是在 devx 上找到一些无源引用(假设它是无源的)。【参考方案7】:JDBC 隐式地划分了您在与事务的连接上执行的每个 查询/更新。您可以通过调用 setAutoCommit(false) 来关闭自动提交模式并调用 commit()/rollback() 来指示事务结束来自定义此行为.伪代码
try
con.setAutoCommit(false);
//1 or more queries or updates
con.commit();
catch(Exception e)
con.rollback();
finally
con.close();
现在,您展示的方法中有一个类型。应该是setTransactionIsolation(int level),不是事务划界的api。它管理一个操作所做的更改如何/何时对其他并发操作可见,ACID 中的 "I" (http://en.wikipedia.org/wiki /Isolation_(database_systems))
【讨论】:
哪里说 setAutoCommit(false) 表示事务的开始? 您的回答并未表明何时可以安全调用 setTransactionIsolation()。为什么 Javadoc 会显示“注意:如果在事务期间调用此方法,则结果是实现定义的。”? 还有一个提示:保持友好并恢复 AutoCommit 的先前状态: boolean previousAutoCommit = conn.getAutoCommit();最后尝试 ... conn.setAutoCommit(previousAutoCommit) @voho 为什么要恢复到以前的AutoCommit?即使它是池化的,在 con.close() 上,它也会回到池中,并且 autoCommit 将在您下次从池中获得连接时变为 true。 如果没有则将连接池更改为HicariCP【参考方案8】:实际上,this page from the JDBC tutorial 会更好读。 你会得到你的连接,设置你的隔离级别,然后做你的更新和东西,然后提交或回滚。
【讨论】:
即使教程也没有明确解决这个问题。它包含一些令人困惑的句子,例如:“建议仅在事务模式期间禁用自动提交模式。”什么是“交易模式”? @Gili 你在哪里被咬了?以上是关于如何在 JDBC 中启动事务?的主要内容,如果未能解决你的问题,请参考以下文章