SQLAlchemy 基礎知識 - autoflush 和 autocommit(轉)

原文:https://zhuanlan.zhihu.com/p/48994990

作者:Cosven

來源:知乎

這篇文章致力於解決以下疑問(本文以 MySQL 爲例):

  1. SQLAlchemy 的 session 是指什麼?
  2. session 的 autoflush 參數是幹什麼的,我到底要不要開啓它?
  3. session 的 autocommit 參數又是什麼,它和 autoflush 的區別是什麼?
  4. SQLAlchemy 是在何時發送 SQL 語句的?

附:

  1. SQLAlchemy MySQL 調試小技巧

SQLAlchemy 基礎

下面是一段官方 SQLAlchemy 使用示例,我們從這個例子出發,認識 SQLAlchemy。

from sqlalchemy import create_engine
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker


# sqlite3/mysql/postgres engine
# 請先自己在 MySQL 中創建一個名爲 test_tmp 的 database
engine = create_engine('mysql://root@localhost/test_tmp', echo=False)

Base = declarative_base()

Session = sessionmaker(bind=engine)

session1 = Session()
session2 = Session()

SessionNoAutoflush = sessionmaker(bind=engine, autoflush=False)
session3 = SessionNoAutoflush()


class User(Base):
    __tablename__  = 'user'

    id = Column(Integer, primary_key=True)
    name = Column(String(64))

session 是什麼?

目前還不知道怎樣直接給 session 下定義,但是我們可以通過它的一些用途來認識它, 在腦海裏腦補出這個東西。

  1. session 會在需要的時候(比如用戶讀取數據、更新數據時)和數據庫進行通信,獲取數據對象,並有一個池子來維護這些對象,保證你訪問數據時不出現意外的問題
  2. session 和連接(connection) 不等同,session 通過連接和數據庫進行通信
  3. session 是 Query 的入口,當你想要發起查詢的時候,一般用法是:session.Query(Model).filter_by(...).first()

如果不完全理解它,也沒關係,有個大概印象即可,以後碰到具體的問題再具體分析, 到時候就可以針對性解決。

官方介紹 session 的資料:https://docs.sqlalchemy.org/en/20/orm/session_basics.html#what-does-the-session-do

autoflush 參數

首先,學習兩個概念:flush 和 commit。

  • flush 的意思就是將當前 session 存在的變更發給數據庫,換句話說,就是讓數據庫執行 SQL 語句。
  • commit 的意思是提交一個事務。一個事務裏面可能有一條或者多條 SQL 語句
  • SQLAlchemy 在執行 commit 之前,肯定會執行 flush 操作;而在執行 flush 的時候,不一定執行 commit,這個主要視 autocommit 參數而定,後面會詳細講

當 autoflush 爲 True 時(默認是 True),session 進行查詢之前會自動把當前累計的修改發送到數據庫(注意:autoflush 並不是說在 session.add 之後會自動 flush),舉個例子(結合開始的代碼):

# 創建了一個對象,這時,這個對象幾乎沒有任何意義,session 不知道它的存在
>>> user = User(name='cosven')
>>> 
# session1.add 這個對象之後,它被 session 放到它的對象池裏面去了,但這時不會發送任何 SQL 語句給數據庫,數據庫目前仍然不知道它的存在
>>>  session1.add(user)
>>>  
# session1.Query 執行之前,由於 autoflush 是 True,session1 會先執行 session1.flush(),然後再發送查詢語句
# 當 session 進行 flush 操作時,session 會先建立(選)一個和數據庫的連接,然後將創建 user 的 SQL 語句發送給數據庫
# 所以,這個查詢是能查到 user 的
>>> session1.query(User).filter_by(name='cosven').first()
<__main__.User object at 0x1108f04e0>

如果 session 的 autoflush 爲 False 的話,session 進行查詢之前不會把當前累計的修改發送到數據庫,而直接發送查詢語句,所以下面這個查詢是查不到對象的。

>>> session3.add(User(name='haha'))
>>> session3.query(User).filter_by(name='haha').first()  # None

再重複的總結一下:

session.flush 的意義:session 計算自己積累的變更,將變更對應的 SQL 語句發送給數據庫。 autoflush 的意義:session 在進行查詢之前,自動的進行一次 flush 操作。

autocommit 參數

commit 對應的概念是事務(transaction),默認情況下,session 參數 autocommit 的值是 False,SQLAlchemy 也推薦將它設置爲 False。

注:MySQL client 默認是將 autocommit 設爲 True 的,所以我們在 cli 中執行一條 SQL 語句,數據庫的數據就會發生變化

這裏複習一下一個基礎知識點:在一個事務被提交之前,事務裏面的修改只對當前事務可見,其它事務看不見。什麼意思?我們看個例子

# ps: session1 的 autocommit 參數爲 False, autoflush 參數爲 True
# 當 session1 執行 add 操作時,
>>> session1.add(User(name='miao'))

# session1 中是可以查到這個 user 的
>>> session1.query(User).filter_by(name='miao').first()
<__main__.User object at 0x1108f00000>

# session3 中查不到
>>> session3.query(User).filter_by(name='miao').first()  # None

# 讓 session1 提交一下當前的事務
>>> session1.commit()

# 再從 session3 中查
>>> session3.query(User).filter_by(name='miao').first() is not None
True

事務不僅可以提交,還可以 rollback,這裏就不講。

SQLAlchemy MySQL 調試小技巧

爲 MySQL 打開查詢 log

SET GLOBAL log_output = "FILE"; the default.
SET GLOBAL general_log_file = "/path/to/your/mysql.log";
SET GLOBAL general_log = 'ON';

然後在 shell 中 tail -f mysql.log,這樣一來,當 MySQL 收到請求時,你就能看到一條日誌, 這樣可以方便你判斷 session 執行什麼操作時,會發送 SQL 語句,什麼時候建立連接。

日誌示例:

2018-11-08T15:12:41.332513Z    53 Query commit
2018-11-08T15:12:41.333753Z    53 Query rollback
2018-11-08T15:12:45.999996Z    43 Query select * from user

將上面的腳本導入 python 或者 ipython

python -i test.py

 

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