目的
- ORMを使うのに否定的な意見があったので、どういう点がメリットかを記載する。
ORMとは
- ORMとはObject Relational Mapperの略で、関係データベース(特にSQL)をオブジェクト指向プログラミングのオブジェクトとして扱う技術。
- 簡単に言うとSQLをプログラミング言語で使用するための技術
メリット
- QLとプログラミングの親和性を高めることができる。一々、SQLからオブジェクトの変換をする必要がない。
- 関係データベースの抽象化が可能。
- 例えば、SQLAlchemyのColumnにautoincrementがあるが、PostgreSQLはSerial、MySQLはauto incrementとして解釈される。これはSQLAlchemyのdialect(方言)として、具象化された依存ライブラリが使用されるため可能である。これも完全に互換性があるわけではなく、Oracleの場合はIdentityクラスを使うことで代用される。
- カラムの指定はqueryメソッド、WHERE句はfilterメソッド、ORDER BY句はorder_by句などSQLの構成が標準化されている。
- セキュリティ向上につながる。文字列結合によるSQLインジェクションを考慮が減る。
- 静的な関係を記述することが可能。特に外部キーや結合などの関係をあらわすことが可能。
デメリット
- 学習コストが高い。SQLに加えてORMの書き方を覚える必要がある。
- ORMを多用するとパフォーマンスが落ちる可能性あり。
- 複雑な結合は表現しづらい。
- できなくはないが、素直にSQLを書いたほうが早い場合が多い(気がする)
例
from typing import NamedTuple
import pymysql.cursors
class HogeUser(NamedTuple):
user_id:int
user_name:str
connection = pymysql.connect(host='localhost',
user='user',
password='pass',
database='hoge',
cursorclass=pymysql.cursors.DictCursor)
with connection:
with connection.cursor() as cursor:
# Read a single record
sql = "SELECT `user_id`, `user_name` FROM `hoge_user`"
cursor.execute(sql)
hoge_users = [HogeUser(user_id=row["user_id"], user_name=row["user_name"]) for row in cursor.fetchall()]
print(hoge_users)
from typing import Generator
from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.engine.base import Engine
from sqlalchemy.orm import (
DeclarativeMeta,
Session,
declarative_base,
sessionmaker,
)
# データベースエンジンを作成
EngineLocal: Engine = create_engine(
"mysql+pymysql://user:pass@localhost:3306/hoge", echo=True
)
# セッションファクトリを作成
SessionLocal = sessionmaker(
autocommit=False, autoflush=False, bind=EngineLocal
)
# モデルを定義するための基本となるBaseクラスを作成
Base: DeclarativeMeta = declarative_base()
# セッションを依存性として定義
def get_db() -> Generator[Session, None, None]:
conn = EngineLocal.connect()
db = SessionLocal(bind=conn)
try:
yield db
finally:
conn.close()
db.close()
# ORM
class HogeUser(Base):
__tablename__ = "hoge_user"
user_id = Column(Integer(), primary_key=True, autoincrement="auto")
user_name = Column(String(45))
gen = get_db()
for ses in gen:
rows = ses.query(HogeUser).all()
print(rows)