【Python】SQLAlchemy:session何時commit,何時close?

SQLAlchemy:session何時commit,何時close?

參考閱讀:SQLAlchemy - 官方文檔

官方文檔說明了關於什麼是session,以及如何創建session、如何使用session、如何關閉session

參考閱讀 - When do I construct a Session, when do I commit it, and when do I close it?

  1. As a general rule, keep the lifecycle of the session separate and external from functions and objects that access and/or manipulate database data. This will greatly help with achieving a predictable and consistent transactional scope.
    一般來說,將會話的生命週期與訪問和/或操作數據庫數據的函數和對象分開。這將極大地幫助實現可預測和一致的事務範圍。

  2. Make sure you have a clear notion of where transactions begin and end, and keep transactions short, meaning, they end at the series of a sequence of operations, instead of being held open indefinitely.
    確保您對事務在何處開始和結束有一個清晰的概念,並保持事務簡短,即它們在一系列操作中結束,而不是無限期地保持打開狀態

官方示例(注意下面包含:不推薦示例、及推薦示例)

E.g. don’t do this(不推薦示例):

### this is the **wrong way to do it** ###

class ThingOne(object):
    def go(self):
        session = Session()
        try:
            session.query(FooBar).update({"x": 5})
            session.commit()
        except:
            session.rollback()
            raise

class ThingTwo(object):
    def go(self):
        session = Session()
        try:
            session.query(Widget).update({"q": 18})
            session.commit()
        except:
            session.rollback()
            raise

def run_my_program():
    ThingOne().go()
    ThingTwo().go()

推薦示例1:Keep the lifecycle of the session (and usually the transaction) separate and external:
鄙人不專業翻譯爲如下:保持session的生命週期,將操作和session獨立起來(管理)
理解爲:類似於將session統一管理,操作的時候,直接把session傳遞過去。

### this is a **better** (but not the only) way to do it ###

class ThingOne(object):
    def go(self, session):
        session.query(FooBar).update({"x": 5})

class ThingTwo(object):
    def go(self, session):
        session.query(Widget).update({"q": 18})

def run_my_program():
    session = Session()
    try:
        ThingOne().go(session)
        ThingTwo().go(session)

        session.commit()
    except:
        session.rollback()
        raise
    finally:
        session.close()

推薦示例2:The most comprehensive approach, recommended for more substantial applications, will try to keep the details of session, transaction and exception management as far as possible from the details of the program doing its work. For example, we can further separate concerns using a context manager:
簡單翻譯:即通過上下文管理來管理

### another way (but again *not the only way*) to do it ###

from contextlib import contextmanager

@contextmanager
def session_scope():
    """Provide a transactional scope around a series of operations."""
    session = Session()
    try:
        yield session
        session.commit()
    except:
        session.rollback()
        raise
    finally:
        session.close()


def run_my_program():
    with session_scope() as session:
        ThingOne().go(session)
        ThingTwo().go(session)

StackOverflow 上關於如何關閉 SQLAlchemy session 的討論:

回答1:StackOverflow - How to close a SQLAlchemy session?

如何正確的關閉 SQLAlchemy session?
StackOverflow 上面關於此問題的回答:
在這裏插入圖片描述

回答2:StackOverflow - How to close a SQLAlchemy session?

如何正確的關閉 SQLAlchemy session?
再看一個 StackOverflow 上面關於此問題的回答:
在這裏插入圖片描述在這裏插入圖片描述

回答說:That is, the Engine is a factory for connections as well as a pool of connections, not the connection itself. When you say conn.close(), the connection is returned to the connection pool within the Engine, not actually closed.

簡單翻譯:也就是說,Engine 相當於一個創建連接的工廠,而不是連接本身。當使用conn.close()時,連接被放回到Engine的連接池當中,而不是真正的關閉了。

如果想要在調用conn.close()時,真正的關閉連接,可以使用poolclass=NullPool屬性:

from sqlalchemy.pool import NullPool
db = create_engine('mysql://root@localhost/test_database', poolclass=NullPool)

拓展閱讀MySQL server has gone away 原因分析及解決方式

conn.close() 是把連接放回連接池,不是真正的關閉;池子裏的空閒連接在MySQL線程裏sleep,長時間不操作,MySQL把連接一端關閉了,所以第二天SQLAlchemy再用這個連接的時候,拋出MySQL server has gone away…

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章