【问题标题】:Why doesn’t SQLite3 require a commit() call to save data?为什么 SQLite3 不需要调用 commit() 来保存数据?
【发布时间】:2011-01-15 12:36:08
【问题描述】:

我在某处读到,要在 Python 中将数据保存到 SQLite3 数据库,应该调用连接对象的方法 commit。然而我从来不需要这样做。为什么?

【问题讨论】:

    标签: python transactions sqlite autocommit


    【解决方案1】:

    这意味着您的 SQLite3 数据库驱动程序以自动提交模式运行。

    提交模式

    database transaction 是一个恢复单元。在事务性database engines 中,所有 SQL 语句都在数据库事务中执行。

    • 当一条SQL语句没有包含在一对start-transaction(BEGINSAVEPOINT)和end-transaction(COMMITROLLBACKRELEASE)SQL语句中时,它会在由 SQL 语句的边界隐式分隔的数据库事务。据说 SQL 语句处于自动提交模式,因为它的数据库事务是自动分隔的。

    • 当一条 SQL 语句包含在一对 start-transaction(BEGINSAVEPOINT)和 end-transaction(COMMITROLLBACKRELEASE)SQL 语句中时,它会在数据库事务显式由一对 SQL 语句分隔。据说该 SQL 语句处于手动提交模式,因为它的数据库事务是手动分隔的。

    换句话说,在数据库引擎级别,自动提交模式是默认的。

    最佳做法是always use the manual commit mode,因为通过将 SQL 语句显式分组到数据库事务中,可以避免数据损坏,因为恢复单元按预期分隔。

    Database drivers 位于数据库引擎之上,因此可以转换它们发送到底层数据库引擎的 SQL 语句。数据库驱动程序通常在任何连接和结束事务(COMMITROLLBACK)SQL 语句(即在任何尚未在数据库事务中的 SQL 语句之前说)。这样,喜欢使用自动提交模式的用户必须明确告诉数据库驱动程序。

    换句话说,在数据库驱动程序级别,手动提交模式通常是默认的。

    SQLite 数据库引擎

    SQLite database engine 默认以自动提交模式运行:

    测试自动提交模式

    int sqlite3_get_autocommit(sqlite3*);

    如果给定的数据库连接处于或不处于自动提交模式,sqlite3_get_autocommit() 接口将分别返回非零或零。自动提交模式默认开启。 BEGIN 语句禁用自动提交模式。自动提交模式由 COMMITROLLBACK 重新启用。

    如果多语句事务中的语句出现某些类型的错误(错误包括 SQLITE_FULLSQLITE_IOERRSQLITE_NOMEMSQLITE_BUSYSQLITE_INTERRUPT),则事务可能会自动回滚.判断SQLite是否在出错后自动回滚事务的唯一方法就是使用这个函数。

    如果另一个线程在该例程运行时更改了数据库连接的自动提交状态,则返回值未定义。

    另请参阅对象、常量和函数列表。

    SQLite3 数据库驱动

    PEP 249 要求 Python 数据库驱动程序默认以手动提交模式运行:

    .commit()

    向数据库提交任何待处理的事务。

    请注意,如果数据库支持自动提交功能,则必须 最初是关闭的。可以提供接口方法将其转回 开。

    不支持事务的数据库模块应该实现 此方法具有 void 功能。

    因此,SQLite3 database driver 默认以手动提交模式运行:

    控制交易

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

    autocommit模式表示修改数据库的语句立即生效。 BEGINSAVEPOINT 语句禁用autocommit 模式,而COMMITROLLBACKRELEASE 结束最外层事务,重新打开autocommit 模式。

    Python sqlite3 模块默认发出BEGIN 语句 隐式在数据修改语言 (DML) 语句之前(即 INSERT/UPDATE/DELETE/REPLACE)。

    您可以通过connect() 调用的isolation_level 参数来控制sqlite3 隐式执行的BEGIN 语句类型,或者 通过连接的isolation_level 属性。如果您指定否 isolation_level,使用普通的BEGIN,相当于 指定DEFERRED。其他可能的值为IMMEDIATEEXCLUSIVE.

    您可以通过将isolation_level 设置为None 来禁用sqlite3 模块的隐式事务管理。这将使底层sqlite3 库在autocommit 模式下运行。然后,您可以通过在代码中显式发出 BEGINROLLBACKSAVEPOINTRELEASE 语句来完全控制事务状态。

    在 3.6 版中更改: sqlite3 用于在 DDL 语句之前隐式提交打开的事务。现在已经不是这样了。

    注意。 — 对于backwards compatibility reasons,SQLite3 数据库驱动程序仅在数据修改之前启动手动提交模式(INSERTUPDATEDELETEREPLACE) SQL 语句,不在数据定义 (CREATE, DROP) 或数据查询 (SELECT) SQL 语句之前,它不是PEP 249 兼容和will hopefully be addressed soon

    示例

    以下 Python 程序说明了在 SQLite3 数据库驱动程序中使用手动提交模式与自动提交模式的含义:

    import sqlite3
    
    # Manual commit mode (the default).
    
    connection = sqlite3.connect("test.sqlite")
    cursor = connection.cursor()
    cursor.execute("CREATE TABLE IF NOT EXISTS t (i INT)")  # sent as is (DDL)
    cursor.execute("INSERT INTO t VALUES (?)", (5,))  # sent as BEGIN; … (DML)
    cursor.close()
    connection.close()  # connection closed without COMMIT statement (common error)
    
    connection = sqlite3.connect("test.sqlite")
    cursor = connection.cursor()
    cursor.execute("SELECT * FROM t")  # table persisted (full transaction)
    assert cursor.fetchall() == []  # data did not persist (partial transaction)
    cursor.close()
    connection.close()
    
    # Auto-commit mode.
    
    connection = sqlite3.connect("test.sqlite", isolation_level=None)
    cursor = connection.cursor()
    cursor.execute("CREATE TABLE IF NOT EXISTS t (i INT)")  # sent as is
    cursor.execute("INSERT INTO t VALUES (?)", (5,))  # sent as is
    cursor.close()
    connection.close()
    
    connection = sqlite3.connect("test.sqlite", isolation_level=None)
    cursor = connection.cursor()
    cursor.execute("SELECT * FROM t")  # table persisted
    assert cursor.fetchall() == [(5,)]  # data persisted
    cursor.close()
    connection.close()
    

    注意。 — 如果使用 in-memory 数据库而不是 on-disk 数据库(通过传递参数":memory:" 而不是"test.sqlite" 函数sqlite3.connect),因为当连接关闭时,内存中的数据库会被删除。

    【讨论】:

      【解决方案2】:

      可能自动提交已开启,默认为http://www.sqlite.org/c3ref/get_autocommit.html

      【讨论】:

      【解决方案3】:

      连接对象也可以用作自动提交或回滚事务的上下文管理器。 11.13.7.3. on docs.python

      # Successful, con.commit() is called automatically afterwards
      with con:
          con.execute("insert into person(firstname) values (?)", ("Joe",))
      

      【讨论】:

      • 解决此问题的唯一方法是制作此表的字典或列表表示并更改列表。那么当我完成了一些数据库并想保存更改时,通过列表并将其提交到表中?
      • @james,在这个例子中不要使用像 {with con:} 这样的上下文管理器,而是自己提交。如果你不使用 with 语句并且仍然有自动提交,请参阅 Navi 答案和 Ismail 评论
      【解决方案4】:

      Python sqlite3 在“INSERT”或“UPDATE”之前自动发出 BEGIN 语句。之后它会自动提交任何其他命令或 db.close()

      【讨论】:

        【解决方案5】:

        添加isolation_level=None进行连接(Ref

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

        【讨论】:

        • 当 "sqlite." 是当前 Python 标准库的 sqlite3 模块的别名或等价物时,这只是明确告诉 sqlite3 应该打开自动提交模式,这是默认设置反正。默认情况下,它将conn.isolation_level 报告为'',但也不是非零会产生效果。
        • @kxr 实际上是auto-commit is off SQLite 3 Python 库中的默认值。
        • @Maggyero ,不,自动提交默认开启
        • @iperov 不根据official documentation 和我的测试(参见my answer 中给出的程序):'底层sqlite3 库默认在自动提交模式下运行,但Python sqlite3 模块默认情况下没有。'
        猜你喜欢
        • 2011-07-07
        • 2011-12-04
        • 2010-11-20
        • 1970-01-01
        • 2018-09-24
        • 2018-01-03
        • 2019-09-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多