[Java] [MyBatis] I want to map a query for a table with a one-to-many relationship to a nested bean

2 minute read


Take a bug tracking system like JIRA or Redmine for example. The system creates a project for each topic. A ticket is created for each project, and the user can add a comment to the ticket. The ER diagram is as follows.


For the data, let’s say:


Now, let’s think about getting all the projects and their associated ticket comments. Writing a query is easy, but what if I map the results to a nested Java Bean called Project.java? In other words, a query query on a table with a one-to-many relationship What if I want to map a to a nested bean?


@ToString(exclude = {"tickets"})
public class Project {
    private int projectId;
    private String name;
    private List<Ticket> tickets;


@ToString(exclude = {"comments"})
public class Ticket {
    private int ticketId;
    private String content;
    private List<Comment> comments;


public class Comment {
    private int commentId;
    private String content;


Use the functions called resultMap and collection. In the above example, create the following xml.


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<mapper namespace="com.example.demo.mapper.ProjectMapper">
  <select id="findAllProject" resultMap="findAllProjectResultMap">
      project.name as project_name,
      ticket.content as ticket_content,
      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

  <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"/>

In the code below, you can check the SQL execution result by Pretty Print to see how the result is mapped to the Java Bean.


public class CommandlineappliApplication implements CommandLineRunner{

    private ProjectMapper projectMapper;

    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);

The result of actually moving is as follows. You can see that the SQL execution results are mapped as expected for the nested beans.

Project(projectId=1, name=Project 1)
  └ Ticket(ticketId=1, content=Ticket 1-1)
      └ Comment(commentId=1, content=Comment 1-1-1)
      └ Comment(commentId=2, content=Comment 1-1-2)
  └ Ticket(ticketId=2, content=Ticket 1-2)
      └ Comment(commentId=3, content=Comment 1-2-1)
      └ Comment(commentId=4, content=Comment 1-2-2)
Project(projectId=2, name=Project 2)
  └ Ticket(ticketId=3, content=Ticket 2-1)
      └ Comment(commentId=5, content=Comment 2-1-1)
      └ Comment(commentId=6, content=Comment 2-1-2)
  └ Ticket(ticketId=4, content=Ticket 2-2)
      └ Comment(commentId=7, content=Comment 2-2-1)
      └ Comment(commentId=8, content=Comment 2-2-2)

Environmental information


<?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">
<relativePath/> <!-- lookup parent from repository -->
<description>Demo project for Spring Boot</description>