1. 程式人生 > >【mysql】sqlalchemy commit 和 flush

【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,在這一次的連線中,可能會執行多個事務,事務不會影響到連線。