とりあえずの独り言

テーマは特に限定せず、独り言のように

【SQLAlchemy】flushとcommitの違い

session.commit()をすると、session.flush()も一緒に実行するらしくcommit()だけでもいいんじゃないかと思ってた。

だが、commit()だけだと、うまくいかないケースがあったので備考録として残す。 ざっくりというと、思った通りの順番でaddして登録したつもりでもcommit()の時にDBへ送られるsqlの順番は保証できないよというお話

commitとは~、flushとは~と言葉で説明してもわけわからんので、flushを気にする必要があるパターン例でみればわかる気がするので以下掲載

commitについては、DB関連でいっぱい説明あると思うので割愛

flushを意識しないでも問題ないパターン

# マスタに未登録の商品のみ登録する
product_id = info.get('product_id')
product_name = info.get('product_name')
price = info.get('price')

if not db.session.query(Products).filter(Products.product_id == product_id ).first():
   # 商品マスタ登録
    product = Products()
    product.product_id = product_id 
    product.product_name= product_name
    product.price = price 
    db.session.add(product)
    db.session.commit()
  • すでにcommit済みのデータをselectして、利用したりする
  • insert対象が1テーブルだけ、や任意の順番でinsertされようが問題ない 場合など

適切な順番でinsertする必要がある場合(flushする必要あり)

外部キーが設定してある場合など。マスタテーブルのほうにレコードがないと、制約違反で落ちる

product_id = info.get('product_id')
product_name = info.get('product_name')
price = info.get('price')
category_id = info.get('category_id')
category_name = info.get('category_name')

# カテゴリマスタ登録
category = Categories()
category.category_id = category_id
category.category_name = category_name
db.session.add(category)
db.session.flush()  # DBに上のSQLを投げたよーという通知

# 商品マスタ登録
product = Products()
product.product_id = product_id 
product.product_name= product_name
product.price = price 
product.category_id = category_id
db.session.add(product)
db.session.commit()

※ Product.category_id には、Category.category_idへの外部キー制約がついている想定です。

コミット前のinsertデータに対してselectしたい場合(flushする必要あり)

検証はしていないけど、おそらくこのパターンもflushは必要。実際は、何らかのカウントしたり、集計した結果に対して実行はするのだろうけど簡略化のためキー検索を例にしている

# 商品マスタ登録
product = Products()
product.product_id = product_id 
product.product_name= product_name
product.price = price 
product.category_id = category_id
db.session.add(product)
db.session.flush()  

# commit前のinsert情報に対して修正する
if db.session.query(Products).filter(Products.product_id == product_id ).first():
   # ~何らかの処理~(insert結果がおかしかったら,rollbackなどの処理とか)
   # 登録時にflushしていないと、レコードを取得できない

db.session.commit()