【SQLAlchemy】sessionの作成はどの方法がいいのか?考えてみた
- 概要
- sessionクラスを使う
- session_markerクラスを使う
- scoped_sessionクラスを使う(オススメ)
- 補足(auto_flush, auto_commit設定について)
- 補足(トランザクションについて)
概要
SQLAlchemyでは、sessionの作成方法が複数あって結局どれがいいの?っていうのをいろいろ考えてみた。
ちなみに、Flask
などのフレームワークを利用している場合はSQLAlchemy
をラップしたFlask-SQLAlchemy
などがあるのでここら辺を利用するとsession
周りは良しなにやってくれます。
フレームワークを使わないような、ツールやバッチ系などではSQLAlchemy
単体で使うことも多いと思うのでそういう人向けです。
参考:
SQLAlchemyのSession生成方法 - Qiita
【PythonのORM】SQLAlchemyで基本的なSQLクエリまとめ - Qiita
Using the Session — SQLAlchemy 1.3 Documentation
一つ目のリンクと結構重なるところが多かったorz
ただ、結論が若干違うのでパk・・・引用しつつ書き残したい。
sessionクラスを使う
from sqlalchemy import create_engine from sqlalchemy.orm import Session DATABASE = "sqlite:///localdb.sqlite3" engine = create_engine(DATABASE) session1 = Session( autocommit=False, autoflush=False, bind=engine) session2 = Session( autocommit=False, autoflush=False, bind=engine) print(session1) print(session2) print(session1==session2) ## 結果 ## # <sqlalchemy.orm.session.Session object at 0x00000214C12E98C8> # <sqlalchemy.orm.session.Session object at 0x00000214C12E9A48> # False
一番簡単なやり方。
Session
を呼ぶたびに、新しいインスタンスが作られる。
ちなみに、Session
クラスはスレッドセーフではないため、setting.py
に記載して使いまわさないほうがいい。(絶対単一スレッドでしか起動しないなら関係ないが)
session_markerクラスを使う
from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker DATABASE = "sqlite:///localdb.sqlite3" engine = create_engine(DATABASE) session = sessionmaker( autocommit=False, autoflush=False, bind=engine ) session1 = session() session2 = session() print(session1) print(session2) print(session1==session2) ## 結果 ## # <sqlalchemy.orm.session.Session object at 0x000001379B0C9788> # <sqlalchemy.orm.session.Session object at 0x000001379B0C9988> # False
Session
クラスを使うときと同様にsession()を呼ぶたびに新しいインスタンスが作成される。
違いは、毎回設定を渡さなくてもいいということぐらい。
上でも書いたように、Session
クラスはスレッドセーフではないのでSession
インスタンスは使いまわさないほうがいい。 だから、sessionmarker
で設定のみsetting.py
に記載して、それぞれの個所でsession()
を呼ぶという使い方になるんだろうと思う。
scoped_sessionクラスを使う(オススメ)
from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker, scoped_session DATABASE = "sqlite:///localdb.sqlite3" engine = create_engine(DATABASE) session = scoped_session( sessionmaker( autocommit=False, autoflush=False, bind=engine ) ) session1 = session() session2 = session() session.remove() session3 = session() print(session1) print(session2) print(session1==session2) print(session3) print(session1==session3) ## 結果 ## # <sqlalchemy.orm.session.Session object at 0x000002A8E2699848> # <sqlalchemy.orm.session.Session object at 0x000002A8E2699848> # True # <sqlalchemy.orm.session.Session object at 0x000002A8E2699988> # False
今までの2つと異なる点は、session
を呼んでも同一のインスタンスを返してくれること。もちろんsession.remove()
すれば新しいインスタンスを返してくれるようになる。
また、このsession
インスタンスはスレッドローカルを利用しているためマルチスレッドで実行させたいといったときは、これを利用する。
実際に実装するときは、
setting.py
の中にscoped_session
でsessionクラスを定義するdb
パッケージを作るdb
パッケージの__init__.py
でsetting.py
をimport
する
みたいな感じになるんじゃないだろうか?
session のクローズについて
session.close()メソッドでセッションを閉じれるが、scoped_session
クラスで作成したインスタンスを使いまわす形になるのでclose
する必要はなさそう。Session()で都度作成するパターンの場合は、使い終わったらclose
は必要だろうけど、その点でもscoped_session
クラスはよさそう。
補足(auto_flush, auto_commit設定について)
ちなみにauto_flush
, auto_commit
はFalse
でいいんじゃないかなー?
Transactions and Connection Management — SQLAlchemy 1.3 Documentation
auto_commitはlegacy modeとか書いてあるし。
ちなみに、flushとcommitでflushしなきゃいけないパターンについては、
【SQLAlchemy】flushとcommitの違い - とりあえずの独り言
で少し書いた。
補足(トランザクションについて)
トランザクションの開始とかは特に、明記する必要もない。auto_commit
設定がTrue
になっている場合は、明示的にトランザクションを設定する場合もあるらしい。
auto_commit
がFalse
の場合は、コミットした段階で新しいトランザクションが開始される。scoped_session
で作成したインスタンスは、スレッドローカルを使うのでマルチスレッドで動作しても違うトランザクションになるので並列実行しても問題なさそう。(スレッドローカルとか、マルチスレッドとか苦手でいまいち理解しきれていない・・・)