2012-05-02 74 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