いがにんのぼやき

WEBエンジニアのブログ。IT、WEB、バンド、アニメ。

ORMの使う使わないについて

  • とあるコミュニティでORMを使う使わない、使い方の議論があった
  • 自分の考えを整理するためにも今自分がこうしたほうがいいなって運用方法を書き出してみる
  • 個人の好みによるものが大きいので一意見として
  • ここではサーバーアプリケーションでの文脈

ORMってそもそも何

前提知識としてORMとは

  • インピーダンスミスマッチを解消するもの
  • インピーダンスミスマッチとは
    • 概念モデルと論理モデルの違いを埋める
    • アプリケーションの要件にそった概念モデル(Entity)は、論理モデル(テーブル)と1対1になるとは限らない
    • こういったDB側の都合をアプリケーションで意識しないで済むようになるもの

ORMを使うか使わないか

でもDB側の都合を意識しないで済むって言うけど結局パフォーマンスとか考えたら意識しなくちゃいけないよね?
で、ORM使ってWhereとか書き始めたらそれSQL書いているだけだよね?ORMいらなくない?
っていうのが今回のORM使う使わない問題の根幹としてあると思う

サーバーアプリケーションからDBの値を引く

サーバーアプリケーションからプログラムを書いて、DBから値を読み込み、アプリケーションで使用する、その方法は大体こんな感じだと思う

  • SQLを直接書いてORMを用いずマッピング
  • ORMを使用するパターン
    • ORMの機能(クエリビルダーなど)を使用する
    • ORMとSQLを組み合わせる

個人的意見

個人の好みによる ORMでクエリビルダーを有効活用するのが自分の最適解

SQLを直接書いてORMを用いずマッピング

ここからは各方法を実際のコード(C#)で良いところ、悪いところを挙げていく
ORMを使用しないマッピングの場合、

  • SQL書く
  • 何かで実行、結果は配列みたいなものに入る
  • 自分で毎回マッピング
 var sql = $@"
 SELECT
  USER.NAME
  ARTICLE.TITLE
 FROM
  USER
 INNER JOIN
  ARTICLE ON ARTICLE.ID = USER.ID
 WHERE
  ID = @id
 ";
 
 return Connection.Query(sql, new { id });
 
 // 上の呼び出し元
 foreach(result in results)
 {
    var name = user["Name"]
    var title = user["Title"]
    
    // 何かの処理
 }

良いところ

  • クエリを直接かけるのでハイパフォーマンス
  • 特別構文解釈の負荷もかかりにくい

悪いところ

  • 取り回しが悪い

そのままSQLからしっかりデータベースのことを考えてチューニングできるのでいい
でもビジネス要件に耐えられるものではないかなあと思ってしまう
上記は単純なSQLだが複雑度が増し、その複雑度の増したSQLが画面やAPIによって若干の条件が変わるごとに同じSQLを書くことになったりして辛くなるのでは
サーバーアプリケーションにおいてはこれを用いることによって行えるパフォーマンス向上も一般的なサーバーアプリケーションでは早すぎる最適化なのではと思う

ORMとSQL書くパターン

前の直SQLだけの場合と比べるとマッピングが追加されただけ

 var sql = $@"
 SELECT
  USER.NAME
  ARTICLE.TITLE
 FROM
  USER
 INNER JOIN
  ARTICLE ON ARTICLE.ID = USER.ID
 WHERE
  ID = @id
 ";
 
 // Userクラスにマッピングされる
 return Connection.Query<User>(sql, new { id });

良いところ

  • クエリを直接かけるのでハイパフォーマンス

悪いところ

  • 取り回しが悪い

これも前述の取り回しの問題が解決できない

ORMでクエリビルダーで書くパターン

C#だとメソッドで書くパターンとクエリ式というもので書くパターンがある

  // メソッド形式
 var result1 = _context.Users
                .Join(
                _context.Articles,
                user => user.ID,
                article => article.UserId,
                (user, article) => new { user, article })
                .Where(m => m.user.ID == id)
                .Select(m => new { m.user.Name, m.article.Title }).ToArray();
 
 // クエリ式
 var result2 = (from user in _context.Users
                join article in _context.Articles
                on user.ID equals article.UserId
                where user.ID == 1
                select new { user.Name, article.Title }).ToArray();
  

良いところ

  • (静的解析であれば)補完が効く
  • 通化、拡張がしやすい

悪いところ

  • 独自構文、メソッドを覚えなくてはいけない
  • 発行されるSQLを把握しとく必要がある

これのいいところは取得するカラムの絞り込みを後から行いやすかったり、Whereの共通化、追加を行いやすい
JoinやWhereの部分を拡張メソッドとして切り出してあげることも可能

  public static IQueryable<User> HasArticle(this IQueryable<User> query)
 {
     return query.Where(user => user.Articles.Any());
 }
 
 var result = _context.Users
                .HasArtice()
                .ToArray();

みたいな
ここらへんはORMによりけり
C#のクエリ式はSQLそのままに近く、かつプログラミング言語の機能を生かせるのでかなりいいと思っている
リッチなORMであれば上記のようなクエリビルダーを使うこともないかもしれない
ここらへんは後でもう少し深堀したいところ