为什么SQLite不需要commit()来保存数据?

23 投票
5 回答
29015 浏览
提问于 2025-04-16 10:00

我在某个地方看到,要把数据保存到Python中的SQLite数据库里,需要调用一个叫commit()的命令。但是我从来没有需要这样做过。为什么呢?

5 个回答

4

在连接数据库的时候,添加 isolation_level=None 这个选项(参考链接

db = sqlite.connect(":memory:", isolation_level=None)
4

可能是自动提交功能开启了,默认情况下就是这样。你可以在这个链接了解更多信息:http://www.sqlite.org/c3ref/get_autocommit.html

34

这意味着你的SQLite3驱动程序处于自动提交模式

了解提交模式

在事务型数据库管理系统中,事务是一系列数据访问操作的集合,这些操作具有以下特点:

  • 可恢复性(原子性属性):如果事务中途被中止,数据库的状态就像这个事务根本没有执行过一样;
  • 可串行化(隔离性属性):多个并发执行的事务的结果就像是一个接一个顺序执行一样,不会互相影响。

根据ISO/IEC 9075:2011 SQL标准,如果当前没有活动的事务,可以通过开始事务的语句显式地启动一个事务,或者在以下情况下隐式启动:

  • 所有SQL模式语句;
  • 一些SQL事务语句(保存点语句、提交语句、回滚语句);
  • 一些SQL数据语句(打开语句、关闭语句、获取语句、选择语句、插入语句、删除语句、更新语句、合并语句、截断表语句、分配扩展动态游标语句、分配接收游标语句、动态打开语句、动态关闭语句、动态获取语句、直接选择语句、动态单行选择语句、动态删除语句、可准备动态删除语句、动态更新语句、可准备动态更新语句、释放定位符语句、保持定位符语句);
  • 一些SQL动态语句(描述输入语句、描述输出语句、分配描述符语句、释放描述符语句、获取描述符语句、设置描述符语句、释放准备语句)。

事务可以通过提交语句或回滚语句显式结束,或者在事务被中止时隐式结束(参见ISO/IEC 9075-2:2011)。

所以几乎所有的SQL语句都是在一个事务中执行的,而这个事务必须被显式提交才能生效。处于这种提交模式的数据库接口被称为手动提交模式。手动提交模式是一种最佳实践,适合程序(非交互式会话),但对用户(交互式会话)来说可能会比较麻烦。因此,大多数数据库接口还提供自动提交模式以便于交互式会话。在自动提交模式下,除了开始事务语句之外的任何事务启动语句之前隐式启动的事务会在语句执行后自动提交,而通过开始事务语句显式启动的事务则必须显式提交才能生效。

数据库接口是特定于数据库引擎的,因此通常使用更通用的接口与数据库引擎进行交互(例如开放数据库连接Java数据库连接Python数据库API)。通用数据库接口适配到特定数据库接口的过程由数据库驱动程序提供。SQLite引擎有一个特定的C语言数据库接口。SQLite3驱动程序将Python数据库API适配到SQLite API。

SQL语句由数据库引擎解释。因此,当数据库引擎和数据库驱动程序处于相同的提交模式(手动提交模式或自动提交模式)时,数据库驱动程序可以将未修改的SQL语句传递给数据库引擎。然而,当它们处于不同的提交模式时,数据库驱动程序必须要么配置数据库引擎以匹配数据库驱动程序的提交模式,要么在将SQL语句传递给数据库引擎之前对其进行转换,以模拟数据库驱动程序的提交模式:

  • 数据库驱动程序通过在每次连接打开、提交语句和回滚语句后隐式地发出一个开始事务语句来模拟手动提交模式,以防止数据库引擎在自动提交模式下隐式提交事务,并在每次连接关闭之前隐式地发出一个回滚语句,以强制数据库引擎回滚最后一个事务(例如A; B; START TRANSACTION; C; D; COMMIT; E; F;被转换为START TRANSACTION; A; B; START TRANSACTION; C; D; COMMIT; START TRANSACTION; E; F; ROLLBACK;)。
  • 数据库驱动程序通过在每个事务启动语句(除了开始事务语句)和开始事务语句、提交语句或回滚语句内的语句之后隐式地发出一个提交语句来模拟自动提交模式,以使事务在手动提交模式下被数据库引擎显式提交(例如A; B; START TRANSACTION; C; D; COMMIT; E; F;被转换为A; COMMIT; B; COMMIT; START TRANSACTION; C; D; COMMIT; E; COMMIT; F; COMMIT;)。

SQLite引擎始终处于自动提交模式(参见SQLite文档)。SQLite3驱动程序默认处于手动提交模式,因此需要模拟它(参见SQLite3文档)。目前,SQLite3驱动程序并没有像前面所述那样正确模拟手动提交模式,而是在每个事务启动的SQL数据语句(除了选择语句)之前隐式发出一个开始事务语句,因此SQL模式语句和选择语句并不总是在显式启动的事务中(参见SQLite3实现)。这不符合Python数据库API的规范,因此在Python 3.12中,遗留的手动提交模式和通过Connection类的isolation_level属性配置的自动提交模式将被弃用,取而代之的是在Connection类中引入一个新的autocommit属性,以配置符合Python数据库API的手动提交模式和自动提交模式(参见CPython问题 #83638CPython拉取请求 #93823)。

示例 1. — 这个Python 3.11程序使用SQLite3驱动程序在遗留的手动提交模式下运行。

import sqlite3

connection = sqlite3.connect(':memory:', isolation_level='DEFERRED')
# No transaction is explicitly initiated here by a start transaction statement.
assert connection.in_transaction is False
statements = []
connection.set_trace_callback(statements.append)
cursor = connection.cursor()
# Transaction 1 is implicitly initiated here.
cursor.execute('CREATE TABLE t (i INT)')
# Transaction 1 is implicitly committed here.
# Transaction 2 is explicitly initiated here by a start transaction statement.
cursor.execute('INSERT INTO t VALUES (?)', (1,))
cursor.execute('CREATE TABLE u (j INT)')
cursor.execute('INSERT INTO u VALUES (?)', (2,))
cursor.close()
connection.close()
# Transaction 2 is implicitly rolled back here.
assert statements == [
    'CREATE TABLE t (i INT)',
    'BEGIN DEFERRED',
    'INSERT INTO t VALUES (1)',
    'CREATE TABLE u (j INT)',
    'INSERT INTO u VALUES (2)',
]

示例 2. — 这个Python 3.12程序使用SQLite3驱动程序在手动提交模式下运行。

import sqlite3

connection = sqlite3.connect(':memory:', autocommit=False)
# Transaction 1 is explicitly initiated here by a start transaction statement.
assert connection.in_transaction is True
statements = []
connection.set_trace_callback(statements.append)
cursor = connection.cursor()
cursor.execute('CREATE TABLE t (i INT)')
cursor.execute('INSERT INTO t VALUES (?)', (1,))
cursor.execute('CREATE TABLE u (j INT)')
cursor.execute('INSERT INTO u VALUES (?)', (2,))
cursor.close()
connection.close()
# Transaction 1 is explicitly rolled back here by a rollback statement.
assert statements == [
    'CREATE TABLE t (i INT)',
    'INSERT INTO t VALUES (1)',
    'CREATE TABLE u (j INT)',
    'INSERT INTO u VALUES (2)',
    'ROLLBACK',
]

示例 3. — 这个Python 3.11程序使用SQLite3驱动程序在遗留的自动提交模式下运行。

import sqlite3

connection = sqlite3.connect(':memory:', isolation_level=None)
# No transaction is explicitly initiated here by a start transaction statement.
assert connection.in_transaction is False
statements = []
connection.set_trace_callback(statements.append)
cursor = connection.cursor()
# Transaction 1 is implicitly initiated here.
cursor.execute('CREATE TABLE t (i INT)')
# Transaction 1 is implicitly committed here.
# Transaction 2 is implicitly initiated here.
cursor.execute('INSERT INTO t VALUES (?)', (1,))
# Transaction 2 is implicitly committed here.
# Transaction 3 is implicitly initiated here.
cursor.execute('CREATE TABLE u (j INT)')
# Transaction 3 is implicitly committed here.
# Transaction 4 is implicitly initiated here.
cursor.execute('INSERT INTO u VALUES (?)', (2,))
# Transaction 4 is implicitly committed here.
cursor.close()
connection.close()
assert statements == [
    'CREATE TABLE t (i INT)',
    'INSERT INTO t VALUES (1)',
    'CREATE TABLE u (j INT)',
    'INSERT INTO u VALUES (2)',
]

示例 4. — 这个Python 3.12程序使用SQLite3驱动程序在自动提交模式下运行。

import sqlite3

connection = sqlite3.connect(':memory:', autocommit=True)
# No transaction is explicitly initiated here by a start transaction statement.
assert connection.in_transaction is False
statements = []
connection.set_trace_callback(statements.append)
cursor = connection.cursor()
# Transaction 1 is implicitly initiated here.
cursor.execute('CREATE TABLE t (i INT)')
# Transaction 1 is implicitly committed here.
# Transaction 2 is implicitly initiated here.
cursor.execute('INSERT INTO t VALUES (?)', (1,))
# Transaction 2 is implicitly committed here.
# Transaction 3 is implicitly initiated here.
cursor.execute('CREATE TABLE u (j INT)')
# Transaction 3 is implicitly committed here.
# Transaction 4 is implicitly initiated here.
cursor.execute('INSERT INTO u VALUES (?)', (2,))
# Transaction 4 is implicitly committed here.
cursor.close()
connection.close()
assert statements == [
    'CREATE TABLE t (i INT)',
    'INSERT INTO t VALUES (1)',
    'CREATE TABLE u (j INT)',
    'INSERT INTO u VALUES (2)',
]

撰写回答