Dav*_*Bob 4 postgresql transaction error-handling
只是想了解这里的想法......
此错误说明对于其他数据库,您可以在发生 SQLException 后运行其他命令。它写于 2001 年,但从未接触过。我们正在使用 Postgres 和 SQL Server,所以现在我们遇到了这个问题。
假设你有:
start transaction;
create table test(id int primary key);
insert into test values (1);
commit;
-- Following statement throws a SQLException(duplicate key) in
-- PG, SS and ORacle
insert into test values (1);
-- Following statement behaves differently for different DBMS:
-- SS and OR: No error...statement runs fine
-- PG: Another SQLException thrown...must rollback or commit
insert into test values (99);
Run Code Online (Sandbox Code Playgroud)
谁能帮助我理解为什么 Postgres 不允许这样做?或者更好的是,是否有连接标志来改变行为?(我看了看,没有找到任何东西。)
我在上面添加了“开始事务”位以表明它不是自动提交的 tx。
这是一个 Java 应用程序,因此我们使用 PG 9.0 JDBC 驱动程序。我已经使用相同的驱动程序通过查询工具测试了上述内容,以将我们的应用程序排除在外。上面的第二个插入语句产生:
错误:当前事务被中止,命令被忽略直到事务块结束
[SQL 状态:25P02]
我在你的例子中遇到的问题是你在谈论 JDBC 行为,但也使用了显式的“启动事务”等命令,这似乎有点冲突,因为我希望你使用 JDBC 的自动提交模式来管理交易。
如果您处于自动提交模式,那么两个插入将分别在自己的事务中,并且第一个的 SQLException 抛出不会影响第二个。
如果您没有处于自动提交模式,那么在第一次插入之前会生成一个隐式的“开始事务”,并且在事务回滚之前无法处理第二次插入。这种行为与您使用 psql 执行脚本时完全不同。
(JDBC 没有指定驱动程序/连接是否应该默认为自动提交打开或关闭,您应该始终明确设置它)
Postgresql 将处理语句的任何错误视为立即中止事务——本质上类似于XACT_ABORTSQL Server 中的模式。其目的是,如果您将一系列命令作为事务提交,则每个命令都依赖于前一个命令,因此任何一个命令的失败都会使所有后续命令无效。
如果这不是您在事务中想要的行为,您需要通过创建保存点来包围可能中止的更新,并在出现错误时回滚到该保存点。
当心查看非常古老的行为讨论(十多年以前的错误肯定很重要),因为在 Postgresql 历史上的某个时刻,有一个名为 的会话变量autocommit,而行为可能完全不同。该变量现在已经消失了,取而代之的是(据我所知)数据库的概念或 JDBC 驱动程序自动将命令包装在事务中(因此实际上并没有真正与 postgresql 进行非事务性交互这样的事情)。
当您使用 psql 执行您建议的脚本时,会发生以下情况:
steve@steve@[local] =# start transaction;
START TRANSACTION
steve@steve@[local] *=# create table test(id int primary key);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "test_pkey" for table "test"
CREATE TABLE
steve@steve@[local] *=# insert into test values (1);
INSERT 0 1
steve@steve@[local] *=# commit;
COMMIT
steve@steve@[local] =#
steve@steve@[local] =# -- Following statement throws a SQLException(duplicate key) in
steve@steve@[local] =# -- PG, SS and ORacle
steve@steve@[local] =# insert into test values (1);
ERROR: duplicate key value violates unique constraint "test_pkey"
DETAIL: Key (id)=(1) already exists.
steve@steve@[local] =#
steve@steve@[local] =# -- Following statement behaves differently for different DBMS:
steve@steve@[local] =# -- SS and OR: No error...statement runs fine
steve@steve@[local] =# -- PG: Another SQLException thrown...must rollback or commit
steve@steve@[local] =# insert into test values (99);
INSERT 0 1
Run Code Online (Sandbox Code Playgroud)
为了获得与您在脚本中编写的相同的行为,您必须在执行插入之前关闭自动提交 - 这会阻止 JDBC 驱动程序在执行下一条语句之前发出隐式“启动事务”。如果您将该隐式生成的事务放入 psql 脚本,则会产生您描述的错误:
steve@steve@[local] =# start transaction; -- generated by JDBC driver
START TRANSACTION
steve@steve@[local] *=# -- Following statement throws a SQLException(duplicate key) in
steve@steve@[local] *=# -- PG, SS and ORacle
steve@steve@[local] *=# insert into test values (1);
ERROR: duplicate key value violates unique constraint "test_pkey"
DETAIL: Key (id)=(1) already exists.
steve@steve@[local] !=#
steve@steve@[local] !=# -- Following statement behaves differently for different DBMS:
steve@steve@[local] !=# -- SS and OR: No error...statement runs fine
steve@steve@[local] !=# -- PG: Another SQLException thrown...must rollback or commit
steve@steve@[local] !=# insert into test values (99);
ERROR: current transaction is aborted, commands ignored until end of transaction block
Run Code Online (Sandbox Code Playgroud)
为了说明这种行为存在的原因,请考虑如果我再次运行第一个事务会发生什么。意图是“创建表并用一行填充它”:
steve@steve@[local] =# start transaction;
START TRANSACTION
steve@steve@[local] *=# create table test(id int primary key);
ERROR: relation "test" already exists
steve@steve@[local] !=# insert into test values (1);
ERROR: current transaction is aborted, commands ignored until end of transaction block
steve@steve@[local] !=# commit;
ROLLBACK
Run Code Online (Sandbox Code Playgroud)
因此,一旦检测到问题(“测试”已经存在),剩余的数据操作就不合适了(无论如何,该行也已经存在)