3
2

More than 3 years have passed since last update.

[MyBatis] 1対多の関係にあるテーブルに対する照会クエリをネストされたBeanにマッピングしたい

Last updated at Posted at 2020-07-31

問題

JIRAやRedmineのようなバグトラッキングシステムを例とします。このシステムでは、トピックごとにプロジェクトを作成します。チケットはプロジェクトごとに作成され、利用者はチケットにコメントを付与することができるとします。ER図は以下の通りです。

image.png

データについては、次のようになっているとします。

image.png

ここで、プロジェクトとそれにひもづくチケット・コメントをすべて取得することを考えます。クエリを書くことは簡単ですが、Project.javaというネストされたJava Beanに結果をマッピングした場合、どのようにすればよいでしょうか? 言い換えれば、1対多の関係にあるテーブルに対する照会クエリをネストされたBeanにマッピングしたい場合、どのようにすればよいでしょうか?

Project.java
@Data
@ToString(exclude = {"tickets"})
public class Project {
    private int projectId;
    private String name;
    private List<Ticket> tickets;
}
Ticket.java
@Data
@ToString(exclude = {"comments"})
public class Ticket {
    private int ticketId;
    private String content;
    private List<Comment> comments;
}
Comment.java
@Data
public class Comment {
    private int commentId;
    private String content;
}

回答

resultMapcollectionという機能を利用します。 上記の例であれば、以下のようなxmlを作成します。

ProjectMapper.xml
<?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="com.example.demo.mapper.ProjectMapper">
  <select id="findAllProject" resultMap="findAllProjectResultMap">
    SELECT
      project.project_id,
      project.name as project_name,
      ticket.ticket_id,
      ticket.content as ticket_content,
      comment.comment_id,
      comment.content as comment_content
    FROM project
    JOIN ticket ON project.project_id = ticket.project_id
    JOIN comment ON ticket.ticket_id = comment.ticket_id
  </select>

  <resultMap id="findAllProjectResultMap" type="com.example.demo.dto.Project">
    <id property="projectId" column="project_id" />
    <result property="name" column="project_name"/>
    <collection property="tickets" ofType="com.example.demo.dto.Ticket">
      <id property="ticketId" column="ticket_id" />
      <result property="content" column="ticket_content"/>
      <collection property="comments" ofType="com.example.demo.dto.Comment">
        <id property="commentId" column="comment_id" />
        <result property="content" column="comment_content"/>
      </collection>
    </collection>
  </resultMap>
</mapper>

以下のコードではSQLを実行結果がどのような形でJava BeanにマッピングされたのかをPretty Printすることで、確認することができます

CommandlineappliApplication.java
@Component
public class CommandlineappliApplication implements CommandLineRunner{

    @Autowired
    private ProjectMapper projectMapper;

    @Override
    public void run(String... args) throws Exception {
        for (Project project : projectMapper.findAllProject()) {
            System.out.printf("%s%n", project);

            for (Ticket ticket : project.getTickets()) {
                System.out.printf("  └ %s%n", ticket);

                for (Comment comment : ticket.getComments()) {
                    System.out.printf("      └ %s%n", comment);
                }
            }
        }
    }
}

実際に動かした結果は次の通り。ネストされたBeanに対して、SQLの実行結果が想定通りマッピングされていることがわかります。

Project(projectId=1, name=プロジェクト1)
  └ Ticket(ticketId=1, content=チケット1-1)
      └ Comment(commentId=1, content=コメント1-1-1)
      └ Comment(commentId=2, content=コメント1-1-2)
  └ Ticket(ticketId=2, content=チケット1-2)
      └ Comment(commentId=3, content=コメント1-2-1)
      └ Comment(commentId=4, content=コメント1-2-2)
Project(projectId=2, name=プロジェクト2)
  └ Ticket(ticketId=3, content=チケット2-1)
      └ Comment(commentId=5, content=コメント2-1-1)
      └ Comment(commentId=6, content=コメント2-1-2)
  └ Ticket(ticketId=4, content=チケット2-2)
      └ Comment(commentId=7, content=コメント2-2-1)
      └ Comment(commentId=8, content=コメント2-2-2)

環境情報

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>mybatis-sample</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>mybatis-sample</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>11</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-batch</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>

        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.batch</groupId>
            <artifactId>spring-batch-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2