MyBatisの自己結合による多対一クエリ方法

現在、データベースのクエリ要件として、特定のニュースカテゴリのIDを与え、その親カテゴリ、親の親カテゴリといった情報を再帰的に取得する必要があります。

データベースのテーブル設計は以下の通りです。

エンティティクラスの設計:

package model;

public class Category {
  private Integer id;
  private String name;
  private Category parent;

  public Integer getId() {
    return id;
  }

  public void setId(Integer id) {
    this.id = id;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Category getParent() {
    return parent;
  }

  public void setParent(Category parent) {
    this.parent = parent;
  }

  @Override
  public String toString() {
    return "Category [id=" + id + ", name=" + name + ", parent=" + parent + "]";
  }
}

SQLクエリインターフェースの定義:

public interface ICategoryDao {
  Category selectCategoryWithParent(int categoryId);
}

SQLはmapper.xmlファイルで定義されています。クエリは"selectCategoryWithParent"というIDのSQLを呼び出し、結果をマッピング(resultMap)しています。resultMapのIDは"categoryMapper"です。"categoryMapper"にはidnameのマッピングに加え、<association/>要素が含まれており、ここで関連関係が定義されています。

  • property="parent": マッピングするプロパティがparentであることを示します。
  • javaType="Category": マッピングする型がCategoryであることを示します。
  • column="parent_id": 親カテゴリを再度クエリするためにparent_idをパラメータとして渡します。
  • select="selectCategoryWithParent": parentプロパティを取得するために実行するSQLステートメントです。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="dao.ICategoryDao">
    <resultMap type="Category" id="categoryMapper">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <association property="parent"
                     javaType="Category"
                     select="selectCategoryWithParent"
                     column="parent_id"/>
    </resultMap>
    <select id="selectCategoryWithParent" resultMap="categoryMapper">
        SELECT id, name, parent_id FROM categories WHERE id = #{categoryId}
    </select>
</mapper>

テストクラス:

import org.apache.ibatis.session.SqlSession;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class CategoryTest {
  private ICategoryDao categoryDao;
  private SqlSession sqlSession;

  @Before
  public void setUp() {
    sqlSession = MyBatisUtils.getSqlSession();
    categoryDao = sqlSession.getMapper(ICategoryDao.class);
  }

  @Test
  public void testSelectCategoryWithParent() {
    Category category = categoryDao.selectCategoryWithParent(7);
    System.out.println(category);
  }

  @After
  public void tearDown() {
    if (sqlSession != null) {
      sqlSession.close();
    }
  }
}

クエリ結果:

[main] DEBUG dao.ICategoryDao.selectCategoryWithParent  - ==>  Preparing: SELECT id, name, parent_id FROM categories WHERE id = ? 
[main] DEBUG dao.ICategoryDao.selectCategoryWithParent  - ==> Parameters: 7(Integer)
[main] DEBUG dao.ICategoryDao.selectCategoryWithParent  - ====>  Preparing: SELECT id, name, parent_id FROM categories WHERE id = ? 
[main] DEBUG dao.ICategoryDao.selectCategoryWithParent  - ====> Parameters: 4(Integer)
[main] DEBUG dao.ICategoryDao.selectCategoryWithParent  - ======>  Preparing: SELECT id, name, parent_id FROM categories WHERE id = ? 
[main] DEBUG dao.ICategoryDao.selectCategoryWithParent  - ======> Parameters: 2(Integer)
[main] DEBUG dao.ICategoryDao.selectCategoryWithParent  - ========>  Preparing: SELECT id, name, parent_id FROM categories WHERE id = ? 
[main] DEBUG dao.ICategoryDao.selectCategoryWithParent  - ========> Parameters: 0(Integer)
[main] DEBUG dao.ICategoryDao.selectCategoryWithParent  - <========      Total: 0
[main] DEBUG dao.ICategoryDao.selectCategoryWithParent  - <======      Total: 1
[main] DEBUG dao.ICategoryDao.selectCategoryWithParent  - <====      Total: 1
[main] DEBUG dao.ICategoryDao.selectCategoryWithParent  - <==      Total: 1
Category [id=7, name=北京金瓯, parent=Category [id=4, name=CBA, parent=Category [id=2, name=体育新闻, parent=null]]]

タグ: MyBatis Java SQL Mapper Association

6月5日 23:48 投稿