【问题标题】:Can I execute query via sqlalchemy without transaction我可以在没有事务的情况下通过 sqlalchemy 执行查询吗
【发布时间】:2019-12-02 19:58:25
【问题描述】:

我正在尝试使用 sqlalchemy 在 Mysql 数据库上执行存储过程。

它在 shell 中运行良好,但抛出此错误:

OperationalError: (MySQLdb._exceptions.OperationalError) (1568, "Transaction characteristics can't be changed while a transaction is in progress")

原因似乎是 SQLAlchemy 在事务中运行查询。并且存储过程中的事务与之冲突。 以下是 sqlalchemy 日志:

2019-07-24 15:20:28,888 INFO sqlalchemy.engine.base.Engine SHOW VARIABLES LIKE 'sql_mode'
2019-07-24 15:20:28,888 INFO sqlalchemy.engine.base.Engine ()
2019-07-24 15:20:28,900 INFO sqlalchemy.engine.base.Engine SELECT DATABASE()
2019-07-24 15:20:28,900 INFO sqlalchemy.engine.base.Engine ()
2019-07-24 15:20:28,910 INFO sqlalchemy.engine.base.Engine show collation where `Charset` = 'utf8mb4' and `Collation` = 'utf8mb4_bin'
2019-07-24 15:20:28,910 INFO sqlalchemy.engine.base.Engine ()
2019-07-24 15:20:28,916 INFO sqlalchemy.engine.base.Engine SELECT CAST('test plain returns' AS CHAR(60)) AS anon_1
2019-07-24 15:20:28,917 INFO sqlalchemy.engine.base.Engine ()
2019-07-24 15:20:28,923 INFO sqlalchemy.engine.base.Engine SELECT CAST('test unicode returns' AS CHAR(60)) AS anon_1
2019-07-24 15:20:28,923 INFO sqlalchemy.engine.base.Engine ()
2019-07-24 15:20:28,928 INFO sqlalchemy.engine.base.Engine SELECT CAST('test collated returns' AS CHAR CHARACTER SET utf8mb4) COLLATE utf8mb4_bin AS anon_1
2019-07-24 15:20:28,928 INFO sqlalchemy.engine.base.Engine ()
2019-07-24 15:20:28,938 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2019-07-24 15:20:28,938 INFO sqlalchemy.engine.base.Engine CALL my_stored_procedure(params);
2019-07-24 15:20:28,938 INFO sqlalchemy.engine.base.Engine ()

我想知道的是我是否可以在没有事务的情况下从 sqlalchemy 运行查询。或者有没有其他方法可以解决问题。我尝试更改存储过程的隔离级别,但这会导致表锁定问题。

【问题讨论】:

    标签: python mysql sqlalchemy flask-sqlalchemy


    【解决方案1】:

    SQLAlchemy 总是尝试在事务中执行查询。但是,可以通过执行COMMIT 语句轻松结束交易。

    首先,您需要一个连接。然后使用该连接发出COMMIT,这将结束新启动的事务。

    这是一个尝试创建新数据库的示例代码,在事务内部运行时会抛出错误。我正在使用 postgres,但使用 MySQL 的相同逻辑也适用。尝试在事务中创建新数据库:

    from sqlalchemy import create_engine
    
    # replace URL with your MySQL instance
    db_url = "postgresql://postgres:secure_pass@localhost:5432/template1"
    engine = create_engine(db_url)
    
    connection = engine.connect()
    
    # running this query in transaction throws an error
    result = connection.execute("CREATE DATABASE temp_db")
    

    会抛出错误:

    ERROR: CREATE DATABASE cannot run inside a transaction block

    现在,添加 COMMIT 将结束由 sqlalchemy 启动的事务:

    from sqlalchemy import create_engine
    
    # replace url with your MySQL instance
    db_url = "postgresql://postgres:secure_pass@localhost:5432/template1"
    engine = create_engine(db_url)
    
    connection = engine.connect()
    
    # commiting will end a transaction
    connection.execute("COMMIT")
    
    # now this query runs fine
    result = connection.execute("CREATE DATABASE temp_db")
    

    并且没有引发错误。

    【讨论】:

    • 这太棒了——而且非常干净!请注意,对于使用Session 对象的任何人,它看起来就像session.connection().execute("COMMIT") 后跟session.connection().execute("CREATE ...") - 即使使用底层连接执行COMMIT,尝试执行session.execute("CREATE ...") 仍然会失败
    • 在使用这种方法进行非事务性调用后,请务必添加session.begin()。如果你之后继续使用会话,我认为你可能会因为没有事务操作而遇到一些问题:) docs.sqlalchemy.org/en/13/orm/…
    • 这个错误对我来说——即使紧跟在session.connection().execute("COMMIT") 之后,尝试session.begin() 也会导致sqlalchemy.exc.InvalidRequestError: A transaction is already begun. Use subtransactions=True to allow subtransactions.。我认为当他们说session 对象总是透明地使用事务时,这就是链接的文档的意思?不过,也许我做错了什么 - 我对 SQLAlchemy 很糟糕。
    • 啊,你完全正确 - 我的错,我无法尝试我写的内容,由于我的误解而发布了这个。实际上,在conn.execute("COMMIT") 对该会话的任何影响事务状态的操作之后,都会根据需要悄悄地启动一个新事务。所以是的 - 在此类提交之后继续使用该会话可能是安全的。
    • 完美!我开始认为这是我的用户错误哈哈
    猜你喜欢
    • 2017-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-01
    • 2015-01-08
    • 2012-04-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多