2012-05-02 40 views
0

这很难描述或显示很多代码,但我会尝试。基本上我有一个多线程的桌面应用程序,将经常处理线程中添加/删除/更改表。从我读的内容来看,我应该使用scoped_session并将其传递给各个线程来完成这项工作(我认为?)。这里有一些基本的代码示例:线程会话在SQLAlchemy上过期吗?

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. 

这就像我所知道的一样多,我认为我能描述的最好。任何想法,我在假设要在这里做?这对我来说都是泥巴。提前致谢!

回答

1

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

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

发动机和scoped_session代表一个称为图案​​- 它们是创造一些其他有用的目的,在这种情况下scoped_session创建Session,并且EngineSession内部使用建立一个Connection与交谈对象到数据库。将Session对象与Engine,scoped_session一起放在Session对象中 - 您将携带工厂(Enginescoped_session)或它们创建的对象本身(Session),这一切都取决于你想要做什么。

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

因此,您需要在此程序中建立的是何时开始执行特定的线程,当数据库进行时需要处理什么,以及何时结束。当你可以建立一个一致的模式时,你会然后链接一个Session这个线程,这是线程的寿命,并且是从来没有共享。此会话产生的所有对象也不能共享给其他线程 - 它们是会话状态的扩展。如果您使用的是“工作线程”,那么这些工作线程应该在自己的Session中根据需要加载自己的数据。会话代表一个实时数据库事务,并且您通常希望事务本地到单个线程。

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

+0

Shucks。那么这是一个巨大的回应,我非常感谢你提供的五段文章作为回应。总而言之,我应该做的是为每一系列需要运行的任务创建一个新的会话?更具体地说,我应该为每个工作线程定义一个_new_(session或scoped_session?)?本质上,我有一个大的线程运行GUI并执行一些小的SQL工作,然后处理一些更复杂/更长时间运行的SQL查询/添加/更新的另一个工作线程。再次感谢您的回应! – Cryptite

+0

另外,使用SQLAlchemy 0.7.7。我认为我理解了你所说的内容,并且更新了OP的代码以反映我现在正在尝试的内容,但是,无论何时执行'session.commit()',_Nonetype都没有属性'expire'_。实际的SQL任务经过并且表得到更新,但提交仍然会产生到期错误。 – Cryptite

+0

如果你有一些一致的东西,然后将它工作成一个简洁的(意思是:非常短,非常短,没有多余的细节)测试用例并将其发布到邮件列表中,我会给它一个运行并提出建议。 – zzzeek