我正在使用SQLAlchemy(和Elixir)用SQLite作为数据库后端在Python中编写应用程序.我使用代码启动一个新的事务session.begin_transaction()
,但是当我调用时,session.rollback()
我收到以下错误:
sqlalchemy.exceptions.OperationalError: (OperationalError) no such savepoint: sa_savepoint_1 u'ROLLBACK TO SAVEPOINT sa_savepoint_1' []
我也收到类似的错误session.commit()
.据我所知,sqlite支持SAVEPOINTS(http://www.sqlite.org/lang_savepoint.html).
如何使嵌套事务生效?
我在Windows上使用Python 3使用嵌套事务遇到了这个问题.我正在使用SQLite版本3.8.11,所以SAVEPOINT
应该支持.显然安装pysqlite不是我的选择,因为它不支持Python 3.
几个小时后我的头撞在桌子上,我在文档中看到了这一部分:
http://docs.sqlalchemy.org/en/latest/dialects/sqlite.html#serializable-isolation-savepoints-transactional-ddl
在Database Locking Behavior/Concurrency一节中,我们引用了pysqlite驱动程序的各种问题,这些问题阻止了SQLite的几个功能正常工作.pysqlite DBAPI驱动程序有几个长期存在的错误,这些错误会影响其事务行为的正确性.在其默认操作模式下,SQLite功能(如SERIALIZABLE隔离,事务DDL和SAVEPOINT支持)不起作用,并且为了使用这些功能,必须采取解决方法.
问题本质上是驱动程序试图猜测用户的意图,无法启动事务并有时过早地结束它们,以尽量减少SQLite数据库的文件锁定行为,即使SQLite本身使用"共享"锁进行读取 - 只有活动.
SQLAlchemy默认选择不改变这种行为,因为它是pysqlite驱动程序的长期预期行为; 如果和当pysqlite驱动程序试图修复这些问题时,这将更多地成为SQLAlchemy默认的驱动程序.
好消息是,通过一些事件,我们可以完全实现事务支持,完全禁用pysqlite的功能并自己发布BEGIN.这是使用两个事件侦听器实现的:
from sqlalchemy import create_engine, event engine = create_engine("sqlite:///myfile.db") @event.listens_for(engine, "connect") def do_connect(dbapi_connection, connection_record): # disable pysqlite's emitting of the BEGIN statement entirely. # also stops it from emitting COMMIT before any DDL. dbapi_connection.isolation_level = None @event.listens_for(engine, "begin") def do_begin(conn): # emit our own BEGIN conn.execute("BEGIN")
添加上面的监听器完全解决了我的问题!
我已经发表了一个完整的工作示例作为要点:
https://gist.github.com/snorfalorpagus/c48770e7d1fcb9438830304c4cca24b9
我还发现记录SQL语句很有帮助(这在上面的例子中使用):
调试(显示)由SQLAlchemy发送到数据库的SQL命令