【问题标题】:When does sqlite3 roll back transactions in python, and when doesn't it?sqlite3什么时候回滚python中的事务,什么时候不回滚?
【发布时间】:2019-11-19 07:15:27
【问题描述】:

我正在创建一个小研讨会来教授如何使用 python 和 SQL,并遇到了这个奇怪的问题。我想展示如何使用with 语句通过 sqlite 创建事务:

import sqlite3

filename = 'data/transaction.db'

print("_________________________")
print("Create Table")
with sqlite3.connect(filename) as conn:
    cursor = conn.cursor()
    sqls = [
        'DROP TABLE IF EXISTS test',
        'CREATE TABLE test (i integer)',
        'INSERT INTO "test" VALUES(99)',
        'SELECT * FROM test']
    for sql in sqls:
        cursor.execute(sql)
        print(cursor.fetchall())

print("_________________________")
print("Create Error with 'with'")        
try:
    with sqlite3.connect(filename) as conn:
        cursor = conn.cursor()
        sqls = [
            'update test set i = 1',
            'SELECT * FROM test',
            'fnord',   # <-- trigger error
            'update test set i = 0',]
        for sql in sqls:
            cursor.execute(sql)
            print(cursor.fetchall())
except sqlite3.OperationalError as err:
    print(err)
    # near "fnord": syntax error

print("_________________________")
print("Show Table")       
with sqlite3.connect(filename) as conn:
    cursor = conn.cursor()
    cursor.execute('SELECT * FROM test')
    for row in cursor:
        print(row)
        # (99,)

这完全符合预期。然而,为了证明没有with 块,执行将在中途完成,我尝试了以下操作:

print("_________________________")
print("Create Error without 'with'")        
conn = sqlite3.connect(filename) 
cursor.execute(  'SELECT * FROM test')
print(cursor.fetchall())    
cursor.execute(  'UPDATE test SET i = 1 WHERE i = 99')
print(cursor.fetchall())    
cursor.execute(  'SELECT * FROM test')
print(cursor.fetchall())    
cursor.execute(  'update test set i = 0')
print(cursor.fetchall())    
cursor.execute(  'SELECT * FROM test')
print(cursor.fetchall())    
conn.close()

print("_________________________")
print("Show Table")       
with sqlite3.connect(filename) as conn:
    cursor = conn.cursor()
    cursor.execute('SELECT * FROM test')
    for row in cursor:
        print(row)
        # (99,)`

整个输出是:

_________________________
Create Table
[]
[]
[]
[(99,)]
_________________________
Create Error with 'with'
[]
[(1,)]
near "fnord": syntax error
_________________________
Show Table
(99,)
_________________________
Create Error without 'with'
[(99,)]
[]
[(1,)]
[]
[(0,)]
_________________________
Show Table
(99,)                            # Why is this not (0,)???

我很困惑为什么最后一个块再次显示 99。最终计划是添加一个 try,except 异常块,以便 SQL 代码模仿第一个块 - 但是我已经很困惑了:)。

感谢澄清

【问题讨论】:

    标签: python sqlite transactions


    【解决方案1】:

    来自python sqlite3 API doc

    底层 sqlite3 库默认以自动提交模式运行, 但是 Python sqlite3 模块默认没有。

    自动提交模式意味着修改数据库的语句采用 立即生效。 BEGIN 或 SAVEPOINT 语句禁用自动提交 模式,以及结束最外层的 COMMIT、ROLLBACK 或 RELEASE 事务,重新打开自动提交模式。

    Python sqlite3 模块默认发出 BEGIN 语句 隐式在数据修改语言 (DML) 语句之前(即 插入/更新/删除/替换)。

    如果连接在没有提交的情况下关闭(或发出显式 ROLLBACK),Python 将回滚事务。此程序中未提交任何事务。

    仅供参考,在“没有'with'的情况下创建错误”块中创建了一个新连接,但没有实例化新游标。

    【讨论】:

      【解决方案2】:

      Python with 语句适用于上下文管理器。尽管一些上下文管理器会释放资源并可能关闭一个对象,但似乎至少对于sqlite3.connection 对象,它只是提交或回滚事务,但不会关闭连接。这可以为DB-API 2.0 interface 确认:

      连接对象可以用作自动提交或回滚事务的上下文管理器。如果发生异常,事务回滚;否则,事务被提交:

      ...

      # Successful, con.commit() is called automatically afterwards
      ...
      # con.rollback() is called after the with block finishes with an exception, the
      # exception is still raised and must be caught
      ...
      # Connection object used as context manager only commits or rollbacks transactions,
      # so the connection object should be closed manually
      

      对于非with 语句块,您永远不会提交事务。当连接关闭时,所有更改都会自动回滚。

      你需要打电话

      conn.commit();
      

      更多详情请见Why the need to commit explicitly when doing an UPDATE?

      附带说明一下,标题为“在没有'with'的情况下创建错误”的代码部分实际上不会导致错误/异常。

      【讨论】:

        猜你喜欢
        • 2010-11-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-17
        • 1970-01-01
        • 2014-10-25
        • 2020-07-16
        相关资源
        最近更新 更多