セッションって何?まずはざっくりイメージ
「セッション」は、データベースとやり取りするときの「ひとまとまりの会話」のようなものです。
Python から見ると、
あるタイミングで DB との“窓口”を開く
その窓口を通して SQL(または ORM)で操作をする
終わったら窓口を閉じる
この「開いているあいだ」が 1 セッション、というイメージです。
特に ORM(SQLAlchemy など)では、Session というクラスが出てきて、
「トランザクションの単位」「オブジェクトと DB の橋渡し役」として、とても重要な役割を持ちます。
ここでは、まず素の DB 接続のセッション感覚から入り、
そのあと SQLAlchemy の Session をしっかり深掘りしていきます。
素の DB 接続での「セッション」の感覚
sqlite3 での接続とカーソルの流れ
Python 標準の sqlite3 を例にすると、基本の流れはこうです。
import sqlite3
conn = sqlite3.connect("app.db") # ここで「接続」を開く
cur = conn.cursor() # SQL を投げるためのカーソルを作る
cur.execute(
"""
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL
)
"""
)
conn.commit() # 変更を確定
conn.close() # 接続を閉じる
Pythonこのとき、connect してから close するまでの間が、
ざっくり「DB とのセッション」と考えられます。
この一連の流れの中で、
複数の SQL を実行したり、トランザクションをまとめたりします。
トランザクションとの関係
例えば、複数の操作をまとめて 1 回のトランザクションにしたい場合は、
同じ接続(同じセッション)の中で行います。
conn = sqlite3.connect("app.db")
cur = conn.cursor()
try:
cur.execute("BEGIN")
cur.execute(
"INSERT INTO users (name, email) VALUES (?, ?)",
("Taro", "taro@example.com"),
)
cur.execute(
"INSERT INTO users (name, email) VALUES (?, ?)",
("Hanako", "hanako@example.com"),
)
conn.commit()
except Exception:
conn.rollback()
raise
finally:
conn.close()
Pythonここでは、connect から close までが「接続としてのセッション」であり、
その中で BEGIN から COMMIT / ROLLBACK までが「トランザクション」です。
セッションは「DB との会話の枠」、
トランザクションは「その中の一連の処理の枠」、
というイメージを持つと整理しやすくなります。
SQLAlchemy の Session をしっかり理解する
Engine と Session の役割の違い
SQLAlchemy では、まず Engine を作ります。
from sqlalchemy import create_engine
engine = create_engine("sqlite:///example.db", echo=True)
PythonEngine は、「どの DB にどう接続するか」という設定と、
実際の接続プール(コネクションプール)を持っている存在です。
一方 Session は、「その Engine を使って、実際に ORM モデルを通して DB を操作する窓口」です。
from sqlalchemy.orm import sessionmaker
SessionLocal = sessionmaker(bind=engine)
session = SessionLocal()
PythonEngine は「物理的な接続の管理者」、
Session は「論理的な会話(トランザクション+オブジェクト管理)の単位」、
と分けて考えるとスッキリします。
Session がやっていること(重要)
SQLAlchemy の Session は、主に次のようなことをしています。
どのオブジェクトが新規追加されたか(INSERT 対象か)を覚えている
どのオブジェクトが変更されたか(UPDATE 対象か)を覚えている
どのオブジェクトが削除されたか(DELETE 対象か)を覚えている
クエリを投げて、結果を ORM モデルのインスタンスとして返す
トランザクションを開始し、commit / rollback を管理する
つまり Session は、「ORM の世界」と「DB の世界」の橋渡し役であり、
かつ「トランザクションの単位」でもあります。
Session を使った基本的な CRUD の流れ
セッションを開く・閉じる
まずは Session を作って、終わったら閉じる、という基本形です。
session = SessionLocal()
# ここで DB 操作をする
session.close()
Python実務では、例外処理も含めて with を使うことも多いですが、
最初はこの「開く→使う→閉じる」の流れを意識するだけで十分です。
INSERT:オブジェクトを追加して commit
User モデルを例にします。
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import declarative_base
Base = declarative_base()
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String, nullable=False)
email = Column(String, unique=True, nullable=False)
Pythonこのモデルを使って、Session 経由で INSERT します。
session = SessionLocal()
new_user = User(name="Taro", email="taro@example.com")
session.add(new_user) # セッションに「新規オブジェクト」として登録
session.commit() # ここで INSERT が実行される
session.close()
Pythonここで重要なのは、add した瞬間に INSERT が走るわけではない、という点です。
Session は「このオブジェクトは新規追加だな」と覚えておき、commit() したタイミングでまとめて DB に反映します。
SELECT:クエリでオブジェクトを取得
Session を使って、User を検索します。
session = SessionLocal()
user = session.query(User).filter_by(email="taro@example.com").first()
print(user.id, user.name, user.email)
session.close()
Pythonsession.query(User) が「User テーブルに対する SELECT」のスタートで、filter_by で WHERE 条件を付け、first() で 1 件だけ取っています。
戻り値は dict ではなく User インスタンスです。
Session は、「DB から取ってきた行を User オブジェクトに変換して返す」役割も担っています。
UPDATE:属性を書き換えて commit
更新も Session を通して行います。
session = SessionLocal()
user = session.query(User).filter_by(email="taro@example.com").first()
user.name = "Taro Yamada" # 属性を書き換える
session.commit() # ここで UPDATE が発行される
session.close()
PythonSession は、「この user オブジェクトは前と違う値になった」と検知し、commit() のときに適切な UPDATE 文を発行します。
DELETE:オブジェクトを delete して commit
削除も同様です。
session = SessionLocal()
user = session.query(User).filter_by(email="taro@example.com").first()
session.delete(user) # 削除対象としてマーク
session.commit() # ここで DELETE が発行される
session.close()
PythonSession は、「このオブジェクトは削除対象だ」と覚えておき、commit() で DELETE を実行します。
Session とトランザクションの関係をもう少し深掘りする
Session は「トランザクションの単位」でもある
SQLAlchemy の Session は、基本的に「1 セッション=1 トランザクション」と考えると分かりやすいです。
Session を作る
複数の add / delete / 属性変更 / クエリを行う
commit する(または rollback する)
Session を閉じる
この一連の流れが、1 つのトランザクションになります。
例えば、ユーザーを作って、そのユーザーに紐づく何かを作る、という処理をまとめたいとき。
session = SessionLocal()
try:
user = User(name="Taro", email="taro@example.com")
session.add(user)
# ここで user を使って別のテーブルにも INSERT したりできる
# 例: Profile(user=user, ...)
session.commit() # ここまで全部成功したら確定
except Exception:
session.rollback() # 途中でエラーがあれば全部取り消し
raise
finally:
session.close()
Pythonこのように、Session を単位として「全部成功か全部失敗か」を制御できます。
これはトランザクション(ACID)の Atomicity と直結しています。
なぜ「Session を短く保つ」ことが大事なのか
Session を長時間開きっぱなしにして、
その中でだらだらと操作を続けると、いくつか問題が出てきます。
トランザクションが長くなり、ロックが長時間保持される
他の処理が待たされる可能性が高くなる
接続プールのコネクションを占有し続けてしまう
そのため、実務では、
必要な処理だけを 1 セッション(1 トランザクション)にまとめる
処理が終わったら早めに commit / rollback して close する
という設計がとても重要になります。
Web アプリでの Session の扱い方イメージ
「1 リクエスト=1 セッション」が基本パターン
FastAPI や Flask などの Web フレームワークと SQLAlchemy を組み合わせるとき、
よく使われるパターンは「1 HTTP リクエストにつき 1 Session」です。
リクエストが来る
Session を作る
そのリクエストの処理の中で DB 操作をする
成功したら commit、エラーなら rollback
最後に Session を閉じる
という流れです。
FastAPI 風に書くと、イメージはこんな感じです。
from fastapi import Depends
def get_db():
db = SessionLocal()
try:
yield db
db.commit()
except Exception:
db.rollback()
raise
finally:
db.close()
@app.get("/users/{user_id}")
def read_user(user_id: int, db: Session = Depends(get_db)):
user = db.query(User).get(user_id)
return {"id": user.id, "name": user.name}
Pythonここで db がまさに Session です。
1 リクエストの間だけ生きていて、終わったら必ず閉じられます。
このパターンを覚えておくと、
「セッションをどの粒度で持つべきか」という感覚が身につきます。
まとめ(セッションは「DB との会話とトランザクションの枠」)
Python × DB × SQL の文脈で「セッション」を整理すると、こうなります。
素の sqlite3 などでは、connect してから close するまでの間が、ざっくり「DB とのセッション」で、その中でトランザクション(BEGIN / COMMIT / ROLLBACK)を行う。
SQLAlchemy の Session は、Engine の上に乗る「論理的な会話の単位」であり、オブジェクトの追加・変更・削除を追跡し、commit でまとめて INSERT / UPDATE / DELETE を流す。
Session はトランザクションの単位でもあり、「全部成功したら commit、途中でエラーなら rollback」という形で、一連の処理を安全にまとめられる。
Session を長く持ちすぎるとロックや接続占有の問題が出るので、「必要な処理だけを短くまとめる(例:1 リクエスト=1 セッション)」という設計が重要になる。
ここまで読んで、
「自分の頭の中にあるアプリの処理を、どんな単位でセッションにまとめるべきか」
少しイメージが湧いてきたと思います。
