ぺい

渋谷系アドテクエンジニアの落書き

O/Rマッパーを使う理由

f:id:tikasan0804:20180805205507p:plain
※サムネがO\Rとなってますが、表記ブレです

記事にした経緯

私の所属するVOYAGE GROUPでは、技術力評価会という、エンジニアがエンジニアを評価する会があります。その発表の素振り、つまり、事前練習会みたいのが行われたりします。 そこで、O/Rマッパーを使っている部分の話になった時に、「O/Rマッパーって何が便利なんですか?なんで使うんですか?SQLを書いたらだめなんですか?」という質問が飛び出しました。

これは私に対して来た質問ではなかったのですが、確かに何で使おうんだろうねっていうのが言語化出来ていなかったので、まとめてみようと思いました。

O/Rマッパーとは

オブジェクト関係マッピング - Wikipedia
Wikipediaには、オブジェクト関係マッピングという名前で紹介されていました。そこに書いてある内容を要約すると・・・

データベースとオブジェクト指向プログラミング言語の間の非互換なデータを変換するプログラミング技法である。

つまり、オブジェクト指向とデータベースの考えの差分を吸収して、どっちでもいい感じに使えるようにする技法。

では、何故この技法が生まれたのかを理解していく。

登場した背景

オブジェクト指向言語で、リレーショナルデータベース(以下データベースとする)を使う時に、取得してきたレコードをオブジェクトにマッピングする作業が必要になります。わからない人向けに手動マッピングのコード例を貼っておきます。

※以下のコードはかなりいい加減なのと、適当にコピってきたなので、流し読みしてください

Connection conn = null;

try {
  conn = DriverManager.getConnection(url, user, password);

  Statement stmt = conn.createStatement();
  String sql = "SELECT id, first_name FROM users";
  ResultSet rs = stmt.executeQuery(sql);
  List<User> users = new ArrayList<User>();
  while(rs.next()) {
      User user = new User();      
      // usersテーブルのid を UserクラスのIdフィールドに入れる(マッピング)
      user.setId(rs.getString("id"));
      user.setFirstName(rs.getString("first_name"));
     users.add(user);
  } 
}catch (SQLException e){
  out.println("SQLException:" + e.getMessage());
}

こういったマッピングを毎クエリ書く必要が出てきます。これはオブジェクト指向言語が悪いとか、データベースが悪いという話ではなく、設計のズレ。これをインピーダンスミスマッチと言います。

  • リレーショナルデータベース設計: 検索や登録更新処理に最適なモデル定義
  • オブジェクト指向設計:データモデルを現実世界のモデルに即したものとして定義

そもそも、思想が違うので、どちらかの思想が合わせるといったことすると、不都合が起きることは当然ですよねっていうことはこの時点でわかる。それらの不都合を解決するために出てきたのがO/Rマッパーです。

O/Rマッパーがない世界感

SQLにはオブジェクト指向を考慮した設計はなされていないため、SQLを扱おうとすると、オブジェクト指向にある柔軟性のメリットが無に帰すわけです。どういうことか?

何かのSQL構築したいと考えた場合、オブジェクトに入れたデータをデータベースに保存するために、一旦オブジェクトからデータを抜き出し、SQLを構築する専用メソッドが必要があります。これだけでもだるい作業なのですが、もし、データベース側でカラム名、型の変更があった時に、SQL作成部分のコードは毎度実装修正する必要が出てきます。(再利用性とは・・・状態)

また、マッピングを書く処理は非常に単調で退屈なコードです。しかし、これを間違えると普通に事故ります。しかも、型が同じであればそれっぽく動いたりもするので、これを人間が手作業で全部やるのは辛い。(コードジェネレーターで解決するという手もありますが、今回は割愛)

使うことで得られるメリット

よくあるO/Rマッパーは、オブジェクトを作成して、データベースのデータを更新するメソッドに渡すだけで、あとはそのメソッド内で、クエリ作成に必要なマッピングを中でやってくれます。
もし、フィールドを増やしたい減らしたいといったことが起きても、updateメソッドに渡すオブジェクトに変更加えるだけで済みます。このメリットはレコードを取得する時にもです。

ちなみに、マッピング情報をxml形式で書いたり、アノテーションで定義をすることが多いです。面倒のように思えますが、そこさえ書けていれば、コードを書く時はオブジェクトを扱うように操作が出来るようになります。

User user = new User();  
user.setId(1);
user.setFirstName("hoge");

// いい感じに変換してくれる(型解釈や構文の構築など)
// UPDATE users SET first_name = 'hoge' WHERE id = 1
db.update(user);

SQLを考えなくてよくなる?

オブジェクト指向言語からオブジェクトをそのまま扱うような感覚でデータベースを扱うことが出来るようにしたのが、O/Rマッパーですが、だからといってSQLを知らなくていいというわけではありません。これはどういったクエリが発行されているのか?は理解しておかないと、「スロークエリ」や「N + 1問題」といった問題に対して何も出来なくなってしまうので、精通しているまではやらなくても、読めるようにしておくことは必須です。

そもそも、O/Rマッパーが登場した背景には、SQL理解しなくても使えるようにではなく、分かっていることを何度も書かないで済むようにしたいや、オブジェクト指向とリレーショナルデータベースのメリットを最大限発揮するために考えられたものなので、知らなくていいやという話ではないのです。

まとめ

世に出ているO/Rマッパーはマッピングだけではなく、付随して便利な機能を持ったものがたくさんあるので、ずばり何がしたいんだっけ?がブレブレだったので、学びがあった。