数据库异常后 SQLAlchemy 回滚中的错误?

Posted

技术标签:

【中文标题】数据库异常后 SQLAlchemy 回滚中的错误?【英文标题】:Bug in SQLAlchemy Rollback after DB Exception? 【发布时间】:2017-04-02 13:23:20 【问题描述】:

我的 SQLAlchemy 应用程序(在 MariaDB 之上运行)包括两个模型 MyModelAMyModelB,其中后者是前者的子记录:

class MyModelA(db.Model):
    a_id   = db.Column(db.Integer, nullable=False, primary_key=True)
    my_field1 = db.Column(db.String(1024), nullable=True)

class MyModelB(db.Model):
    b_id   = db.Column(db.Integer, nullable=False, primary_key=True)
    a_id = db.Column(db.Integer, db.ForeignKey(MyModelA.a_id), nullable=False)
    my_field2 = db.Column(db.String(1024), nullable=True)

这些是我创建的MyModelAMyModelB 的实例:

>>> my_a = MyModelA(my_field1="A1")
>>> my_a.aid
1
>>> MyModelB(a_id=my_a.aid, my_field2="B1")

我有以下代码删除MyModelA 的实例,其中a_id==1

db.session.commit()
try:
    my_a = MyModelA.query.get(a_id=1)
    assert my_a is not None
    print "#1) Number of MyModelAs: %s\n" % MyModelA.query.count()
    db.session.delete(my_a)
    db.session.commit()
except IntegrityError:
    print "#2) Cannot delete instance of MyModelA because it has child record(s)!"
    db.session.rollback()
    print "#3) Number of MyModelAs: %s\n" % MyModelA.query.count()

当我运行这段代码时,看看我得到的意外结果:

#1) Number of MyModelAs: 1
#2) Cannot delete instance of MyModelA because it has child record(s)!
#3) Number of MyModelAs: 0

删除失败,数据库抛出异常,导致回滚。然而,即使在回滚之后,表中的行数表明该行 - 据说没有被删除 - 实际上已经消失了!!!

为什么会这样?我怎样才能解决这个问题?这似乎是 SQLAlchemy 中的一个错误。

【问题讨论】:

你检查过自动提交是否被禁用了吗? 同样的想法:你说你使用 MariaDB。 MariaDB 中的哪种引擎? MyISAM 不支持事务,所以它总是处于“自动提交”模式 sqlalchemy 生成的查询有哪些? SELECTsFOR UPDATE 吗? 【参考方案1】:

TL;DR 您的问题可能与缺少明确的关系声明有关。

例如,here 有一个对象关系的样本。除了使用 ForeignKey 字段之外,该类还明确使用 relationship 指令来定义该连接。在session API documentation 中出现以下文字:

对象引用应该在对象级别构建,而不是在外键级别

这可能暗示了 SQLAlchemy 管理关系的方式。我对底层机制并不十分熟悉,但有可能这就是发生的事情。您的会话仅包含 MyModelA 对象。由于您没有在MyModelB 的定义中使用relationship() 指令,因此MyModelA 类型的对象不知道其他对象可能通过ForeignKey 引用它们这一事实。因此,当会话即将提交时,它不知道删除对象会影响其他一些MyModelB 对象,并且其事务机制没有考虑到这一点。 我建议明确添加关系可能会阻止这种行为。

【讨论】:

以上是关于数据库异常后 SQLAlchemy 回滚中的错误?的主要内容,如果未能解决你的问题,请参考以下文章

Windows服务安装在回滚中结束

SQLAlchemy在FlushError之后没有回滚

代码回滚后需要重新登录app吗

基础入门_Python-模块和包.深入SQLAlchemy之事务回滚与反射还原对象?

事务场景中,抛出异常被catch后,如果需要回滚,一定要手动回滚事务

SqlAlchemy + Tornado:在回滚无效事务之前无法重新连接