「ヘッダ明細型」のデータはアプリを作るうえでよく登場するデータ構造である。 「ヘッダ」が親データ、「明細」が親に紐づく子データを表しており、例えば成績のデータなどで、各生徒の英語と数学の成績を表す場合に生徒の情報を親としてヘッダテーブルに格納、成績情報は親に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");
下図のようなオブジェクトとして読み込まれる。
使いこなせば複雑なデータ構造をシンプルに扱えそうな方法であるが、実際使ってみると気を付けなければならない点もあり、そのような点は次回以降また紹介していこうと思う。