sqlalchemy.exc.ResourceClosedError:在select之后插入时,此Connection将关闭

时间:2015-03-13 11:02:52

标签: python sqlite sqlalchemy

我正在从SQLite数据库中执行select(),然后执行insert()

engine = create_engine('sqlite:///testdb.db')
metadata = MetaData(bind=engine)
test = Table('test', metadata, autoload=True)

# Select all from pending_data
sel = select([test])
res = engine.execute(sel)

print res

# do an insert into pending_data
test.insert()\
    .values(info='blah')\
    .execute()

当我的代码执行插入行时,我收到此错误:

sqlalchemy.exc.ResourceClosedError: This Connection is closed

但是,如果我将我的res转换为如此列表:

res = list(engine.execute(sel))

我的代码运行正常。这是怎么回事?

1 个答案:

答案 0 :(得分:9)

SQLAlchemy有两个必须要注意的概念:连接和引擎。引擎可以支持许多同时连接。在您的示例中,您将表绑定到引擎。现在,每当您调用.execute时,都会为您执行的每个查询创建一个新连接。但sqlite3只允许1个同时"连接"。

解决此问题的最佳方法是创建连接并明确使用它,而不是自动创建的引擎连接;并使用与with语句的连接,以确保在块结束时关闭连接:

engine = create_engine('sqlite:///testdb.db')
metadata = MetaData(bind=engine)
test = Table('test', metadata, autoload=True)

with engine.connect() as connection:
    # Select all from pending_data
    sel = select([test])
    res = connection.execute(sel)

    # do an insert into pending_data
    connection.execute(test.insert().values(info='blah'))

要理解这种行为,会发生错误,因为您在隐式创建并保持的集合中持有活动游标(res变量引用此变量;直到您使用它,或关闭它,或删除它为止引用它,游标,因此连接将是活动的,数据库将被锁定)。

当你执行list(res)时,你正在消耗光标并被SQLAlchemy关闭;如果结果的引用计数将降至0,则会发生同样的情况。

您还可以尝试以下方式来查看要点,它们按您的预期运作:

res = engine.execute(sel)
print(res)
res.close()  # close explicitly

res = engine.execute(sel)
print(res)
del res  # drop the only reference to res

因此,总是完全使用ResultProxy或明确地关闭它,或者在完成后删除对它的引用。

如果您重复使用相同的连接,这不是问题。只有当你创建一个与sqlite3数据库的新连接时(postgresql,mysql,oracle等也处理这个问题)。