我可以在没有事务的情况下通过 sqlalchemy 执行查询吗
Posted
技术标签:
【中文标题】我可以在没有事务的情况下通过 sqlalchemy 执行查询吗【英文标题】:Can I execute query via sqlalchemy without transaction 【发布时间】: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 运行查询。或者有没有其他方法可以解决问题。我尝试更改存储过程的隔离级别,但这会导致表锁定问题。
【问题讨论】:
【参考方案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")
对该会话的任何影响事务状态的操作之后,都会根据需要悄悄地启动一个新事务。所以是的 - 在此类提交之后继续使用该会话可能是安全的。
完美!我开始认为这是我的用户错误哈哈以上是关于我可以在没有事务的情况下通过 sqlalchemy 执行查询吗的主要内容,如果未能解决你的问题,请参考以下文章