2020年4月29日水曜日

MyBatisでヘッダ明細型のデータを扱う

「ヘッダ明細型」のデータはアプリを作るうえでよく登場するデータ構造である。 「ヘッダ」が親データ、「明細」が親に紐づく子データを表しており、例えば成績のデータなどで、各生徒の英語と数学の成績を表す場合に生徒の情報を親としてヘッダテーブルに格納、成績情報は親にidで紐づけて明細テーブルに格納すると以下のようにあらわすことができる。

studentテーブル(ヘッダ)

id name
100 やまだ
101 たなか

resultテーブル(明細)

refid course score
100 英語 80
100 数学 90
101 英語 50
101 数学 68

やまだ君の成績は英語80点で数学90点。たなか君の成績は英語50点で数学68点を表している・・・たなか君はもう少し頑張ったほうがいいですね・・・

このようなデータの持ち方は「Summary」と「Detail」などとも呼ばれたり、呼び方はさまざまであるが、商品カートや伝票など業務の世界でも頻繁に登場する。

このようなデータを扱う場合はStudentクラスがResultクラスのオブジェクトを持つようなネストした構造で表現することができる。 例えばJavaの場合のクラス定義は以下のようになる。setter/getterメソッドは省略して書いているので適宜付与してもらうとよいだろう。

public class Student {
  int id;
  String name;
  Result[] result;
}

public class Result {
  String course;
  int score;
}

ここでMyBatisを使ってstudentテーブルとresultテーブルの内容を上記クラスに読み込むことを考えてみる。studentテーブルをSELECTしてからresultテーブルをSELECTするように2つのSELECTに分けてもよいが、MyBatisではテーブルを結合した1つのSQLでネストしたクラスを読み込むことができる。

mapperでcollectionタグを使いネストした部分を定義する。property属性に子オブジェクトを格納するプロパティ名、oftype属性にそのクラス名を指定する。 SELECTのSQLでは2つのテーブルを結合する。

<mapper namespace="mapper.student">
  <resultmap id="student" type="student">
    <id column="id" property="id" />
    <result column="name" property="name" />

    <collection property="result" oftype="Result">
      <result column="course" property="course" />
      <result column="score" property="score" />
    </collection>
  </resultmap>

  <select id="find" resultmap="student">
    SELECT id, name, course, score
    FROM student, result
    WHERE id = refid
    ORDER BY id
  </select>
</mapper>

SELECTの結果は以下のようにstudentの内容とresultの内容が1行に結合されたものとなるが、親クラスの内容はmapperのidタグで指定された列でまとめられオブジェクトが構築されるようである。

SELECTの結果

id name course score
100 やまだ 英語 80
100 やまだ 数学 90
101 たなか 英語 50
101 たなか 数学 68

以下のコードで読み込みを行うと・・・

List<Student> entities = sqlSession.selectList("mapper.student.find");

下図のようなオブジェクトとして読み込まれる。



使いこなせば複雑なデータ構造をシンプルに扱えそうな方法であるが、実際使ってみると気を付けなければならない点もあり、そのような点は次回以降また紹介していこうと思う。

0 件のコメント:

コメントを投稿