【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確實把資料寫入了資料庫,進行測試:
models.py執行建表語句。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)
然後我在資料庫裡添加了一條記錄,name=“ly”,age=25,沒有貼出這段程式碼。
然後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() 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)
另外開一個程序,讀取資料庫,
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,在這一次的連線中,可能會執行多個事務,事務不會影響到連線。