0

这很难描述或显示很多代码,但我会尝试。本质上,我有一个多线程桌面应用程序,它将经常处理线程中表的添加/删除/更改。根据我的阅读,我应该使用 scoped_session 并将其传递给各个线程来完成工作(我认为?)。下面是一些基本的代码示例:

class SQL():
    def __init__(self):        
        self.db = create_engine('mysql+mysqldb://thesqlserver')
        self.metadata = MetaData(self.db)
        self.SessionObj = scoped_session(sessionmaker(bind=self.db, autoflush=True))

db = SQL()
session = db.SessionObj()
someObj = Obj(val, val2)
session.add(someObj)
session.commit()

上面的类是我用作 SQL 东西的一般访问的。在创建新会话、执行查询并对其进行更新/添加后,在 session.commit() 上,我收到以下错误:

Traceback (most recent call last):
  File "core\taskHandler.pyc", line 42, in run
  File "core\taskHandler.pyc", line 184, in addTasks
  File "core\sqlHandler.pyc", line 35, in commit
  File "sqlalchemy\orm\session.pyc", line 624, in rollback
  File "sqlalchemy\orm\session.pyc", line 338, in rollback
  File "sqlalchemy\orm\session.pyc", line 369, in _rollback_impl
  File "sqlalchemy\orm\session.pyc", line 239, in _restore_snapshot
  File "sqlalchemy\orm\state.pyc", line 252, in expire
AttributeError: 'NoneType' object has no attribute 'expire'

然后下一个如果另一个 sql 尝试通过:

Traceback (most recent call last):
  File "core\taskHandler.pyc", line 44, in run
  File "core\taskHandler.pyc", line 196, in deleteTasks
  File "sqlalchemy\orm\query.pyc", line 2164, in scalar
  File "sqlalchemy\orm\query.pyc", line 2133, in one
  File "sqlalchemy\orm\query.pyc", line 2176, in __iter__
  File "sqlalchemy\orm\query.pyc", line 2189, in _execute_and_instances
  File "sqlalchemy\orm\query.pyc", line 2180, in _connection_from_session
  File "sqlalchemy\orm\session.pyc", line 729, in connection
  File "sqlalchemy\orm\session.pyc", line 733, in _connection_for_bind
  File "sqlalchemy\orm\session.pyc", line 249, in _connection_for_bind
  File "sqlalchemy\orm\session.pyc", line 177, in _assert_is_active
sqlalchemy.exc.InvalidRequestError: This Session's transaction has been rolled back by a nested rollback() call.  To begin a new transaction, issue Session.rollback() first.

这大约是我所知道的,我认为这是我能描述的最好的。关于我应该在这里做什么的任何想法?对我来说都是泥巴。提前致谢!

4

1 回答 1

2

有趣的是,您错过了“从中提取代码”的答案中最关键的部分,即中间有一个 Python 函数,它正在执行一些抽象操作(它被标记为func())。该代码说明了一个函数的事务包装器,在上面的示例中,您改为调用一个对象方法commit(),该方法不会以其他方式调用任何带有Session.

在这里,您有一种称为 SQL() 的会话保持对象,它并没有真正为您的程序增加任何用处,反而使程序变得不必要地复杂,并且可能也是问题的根源。除非您的应用程序打算在不同时间连接到许多不同的数据库,并使用SQL()对象来表示该状态,否则构建一个名为“SQL”的类并带有一个“引擎”并没有多大用处。只需将引擎以及您的 scoped_session() 粘贴在某个模块中即可。

engine 和 scoped_session 代表了一种称为工厂模式的模式——它们是创建一些其他有用对象的对象,在这种情况下 scoped_session 创建一个Session,并且在Engine内部使用Session来创建一个Connection与数据库对话的对象。将对象与andSession一起放置为兄弟成员没有多大意义- 您将随身携带工厂(and )或它们创建的对象本身(the ),这一切都取决于您'正在尝试做。Enginescoped_sessionEnginescoped_sessionSession

本身,请记住,Session这里我们谈论的是工厂创建的东西Session),而不是工厂本身(Enginescoped_session),它根本不是线程安全的。它是你通常只在函数本地创建的东西——它不应该是全局的,如果你实际上SQL()跨线程使用单个对象,这可能是这里的问题。你得到的实际错误,我不太确定那是什么,如果我知道这里使用的 SQLAlchemy 的确切版本,我只能有更好的线索,尽管错误的随机性表明你有某种线程问题,其中一个线程中的某物变为无,因为另一个线程期望存在相同的对象。

所以你需要在这个程序中确定一个特定的执行线程何时开始,它在执行过程中需要对数据库做什么,然后什么时候结束。当您可以为此建立一致的模式时,您可以将单个链接Session到该线程,该线程在该线程的生命周期内有效,并且永远不会共享。此会话生成的所有对象也不得共享给其他线程 - 它们是会话状态的扩展。如果您正在使用“工作线程”,则这些工作线程应根据需要在自己的会话中加载自己的数据。Session 代表一个实时数据库事务,您通常希望事务本地到单个线程。

由于这不是一个 Web 应用程序,您可能希望放弃使用scoped_session,除非您确实有一个地方可以使用线程本地模式。

于 2012-05-07T22:04:05.383 回答