According to the documentation,
连接对象可以用作自动提交或回滚事务的上下文管理器。如果发生异常,则回滚事务;否则,交易承诺:
我知道with
语句中的所有内容都应该是原子事务。现在考虑这段代码
import sqlite3
con = sqlite3.connect(':memory:')
try:
with con:
con.execute('create table foo (id integer primary key)')
con.execute('insert into foo values (1)')
con.execute('insert into foo values (1)')
except sqlite3.Error:
print('transaction failed')
try:
rec = con.execute('select count(*) from foo')
print('number of records: {}'.format(rec.fetchone()[0]))
except sqlite3.Error as e:
print(e)
返回
transaction failed
number of records: 0
一方面,由于重复的值,交易失败。另一方面,表foo
存在,即使它是空的,这意味着第一个插入已经回滚。表创建不应该回滚吗?
“手动”执行交易会产生预期结果:
import sqlite3
con = sqlite3.connect(':memory:')
con.execute('begin')
try:
con.execute('create table foo (id integer primary key)')
con.execute('insert into foo values (1)')
con.execute('insert into foo values (1)')
con.execute('commit')
except sqlite3.Error:
con.execute('rollback')
print('transaction failed')
try:
rec = con.execute('select count(*) from foo')
print('number of records: {}'.format(rec.fetchone()[0]))
except sqlite3.Error as e:
print(e)
回报
transaction failed
no such table: foo
为什么会出现差异?
从Python 3.6,DDL或Data Defitinion Language语句开始,如CREATE TABLE
,不会启动事务。这意味着任何此类语句在您执行时都会自动提交。
见Controlling transactions section:
默认情况下,
sqlite3
模块在数据修改语言(DML)语句(即INSERT
/UPDATE
/DELETE
/REPLACE
)之前隐式打开事务。[...]
在版本3.6中更改:
sqlite3
用于在DDL语句之前隐式提交打开的事务。这已不再是这种情况。
这意味着如果您希望DDL语句成为事务的一部分,则必须显式启动事务。
使用连接作为上下文管理器仍然只在退出时发出提交或回滚,它不会启动事务;相反,遇到的第一个DML语句将启动一个。如果您希望DDL成为事务的一部分,请在顶部添加begin
语句:
try:
with con:
con.execute('begin') # explicit, rather than implicit, transaction start
con.execute('create table foo (id integer primary key)')
con.execute('insert into foo values (1)')
con.execute('insert into foo values (1)')
except sqlite3.Error:
print('transaction failed')