【mysql】sqlalchemy commit 和 flush

今天看到了commit和flush函數,想要弄清楚區別。

先看下對象的狀態。總共5個,這裏只談3個。

transitant:剛new出來的對象,沒有和session或者orm框架產生關聯。

pending:transitant的對象調用add後,就會變爲pending,加入了orm框架的監管範圍。

persistant:調用flush以後就會變味persistant,也就是被寫到了數據庫中。

查詢官網後,發現:

flush會把更改提交到數據庫,commit會默認調用flush,然後標誌這個事務的提交,也就是事務執行完畢。如果只調用flush,那麼更新雖然可以被寫入數據庫,但是事務是不完整的,沒有提交。由於事務隔離型的存在,可能其他的事務是無法看到這次更新操作的。只有調用了commit,才能被看成是事務完整的執行完畢。

爲了驗證flush確實把數據寫入了數據庫,進行測試:

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
 
Base = declarative_base()
class People(Base):
 
    __tablename__ = 'people'
 
    id = Column(Integer, primary_key=True, unique=True, index=True, autoincrement=True)
    name = Column(String(100))
    age = Column(Integer, default=0)
 
    def __repr__(self):
        return "<People(id={}, name={}, age={}')>".format(self.id, self.name, self.age)
 
engine = create_engine('mysql+mysqlconnector://root:@localhost:3306/test', echo=True)
metadata = MetaData(engine)
 
people = Table('people', metadata,
    Column('id', Integer, primary_key=True),
    Column('name', String(100)),
    Column('age', Integer))
metadata.create_all(engine)
models.py執行建表語句。
然後我在數據庫裏添加了一條記錄,name=“ly”,age=25,沒有貼出這段代碼。

from models import People
import sqlalchemy.orm as o
from sqlalchemy import create_engine
import time
from sqlalchemy import inspect
 
 
engine = create_engine('mysql+mysqlconnector://root:@localhost:3306/test', isolation_level='READ_UNCOMMITTED')
#engine = create_engine('mysql+mysqlconnector://root:@localhost:3306/test')
 
Session = o.sessionmaker(bind=engine)
s1 = Session()
 
p1 = People(name='ly1', age=25)
 
s1.add(p1)
s1.flush()
insp = inspect(p1)
print(insp.transient)
print(insp.persistent)
print(insp.pending)
 
#s1.commit()
 
time.sleep(1000000)
然後add.py插入另外一條數據,測試,插入以後,沒有提交,只是flush,然後進程停滯在這裏。


另外開一個進程,讀取數據庫,

from models import People
import sqlalchemy.orm as o
from sqlalchemy import create_engine
import time
from sqlalchemy import inspect
 
 
engine = create_engine('mysql+mysqlconnector://root:@localhost:3306/test', isolation_level='READ_UNCOMMITTED')
#engine = create_engine('mysql+mysqlconnector://root:@localhost:3306/test')
 
Session = o.sessionmaker(bind=engine)
s1 = Session()
 
r1 = s1.query(People).all()
 
print(r1)
最後可以讀到兩條數據。

[<People(id=1, name=ly, age=25')>, <People(id=31, name=ly1, age=25')>]
而且,之前打印了p1的對象狀態,在flush之後是persist的,說明flush確實是寫入數據庫了。

這個實驗需要注意數據庫隔離級別,這裏設置爲了讀未提交,否則其他的事務是讀不到的,因爲之前的事務只有flush沒有commit。比如這時開一個mysql的命令行,就無法讀取到添加的數據,因爲mysql的默認是重複讀。

se

關於這個例子繼續延伸下:如果讀取的進程在寫入的進程執行完畢後纔開始,比如sleep完了。結果會是什麼。

結果是無法讀到add的數據了,這是因爲之前的事務結束了,而且寫入了一條數據,但是該數據的寫入是放在一個事務中的,事務並沒有commit操作,根據事務的原子性特徵,該事務要麼全做,要麼全不做,由於這個事務不完整,所以只能全不做,因此之前的添加不會生效的。

另外補充一個細節,在同一個事務中,可以讀到pending狀態的對象,也就是隻add,再query,可以讀到,這是爲什麼?add不是沒有寫入數據庫嗎?怎麼可以讀到?這是因爲query的時候,默認是要autoflush的,也就是會自動把當前事物add的數據flush到數據庫中,所以就會讀到了。如果設置session的autoflush=Flase,那麼只add然後query,就讀不到add的數據了。這個細節要注意。

結論就是,flush會寫入數據庫,commit會調用flush,並且表示事務結束。

至於事務到底會讀取到什麼樣的數據,要看數據庫隔離級別。


補充:每一次連接就取得了連接池中的一個連接,也就是conncetion的構建,就會建立一個tcp,在這一次的連接中,可能會執行多個事務,事務不會影響到連接。


————————————————
版權聲明:本文爲CSDN博主「絕世好阿狸」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/u010900754/article/details/77145865

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