Master MyBatis resultMap: From Simple Queries to Complex Mappings

This article explains the powerful MyBatis resultMap element, covering basic getter/setter injection, constructor injection, inheritance, one-to-one and one-to-many associations, and discriminators, while providing complete XML examples and Java entity code to help developers map complex relational data efficiently.

Programmer DD
Programmer DD
Programmer DD
Master MyBatis resultMap: From Simple Queries to Complex Mappings

1. Introduction

resultMap is the most important and powerful element in MyBatis. It frees you from writing repetitive JDBC ResultSet extraction code, allowing you to replace thousands of lines of code with a concise mapping definition. Simple statements require zero configuration, while more complex ones only need relationships described.
resultMap

can aggregate complex query results such as multi‑table joins, one‑to‑one or one‑to‑many relationships into a single result set. This article provides a detailed explanation of resultMap with a demo at the end.

2. resultMap

Next we look at how resultMap performs mapping.

2.1 Getter/Setter Injection

We declare an entity class that corresponds to the database:

@Data
public class Employee implements Serializable {
    private static final long serialVersionUID = -7145891282327539285L;
    private String employeeId;
    private String employeeName;
    private Integer employeeType;
}

The corresponding resultMap is:

<mapper namespace="cn.felord.mybatis.mapper.EmployeeMapper">
    <resultMap id="EmployeeMap" type="cn.felord.mybatis.entity.Employee">
        <id column="employee_id" property="employeeId"/>
        <result column="employee_name" property="employeeName"/>
        <result column="employee_type" property="employeeType"/>
    </resultMap>
</mapper>

The attributes are explained as follows:

<mapper namespace="global unique namespace">
    <resultMap id="unique within namespace" type="mapped entity class">
        <id column="primary key column or alias, improves performance" property="entity field"/>
        <result column="database column or alias" property="entity field"/>
    </resultMap>
</mapper>

Injection uses Getter and Setter methods, so the entity must have a no‑arg constructor and corresponding getters/setters.

2.2 Constructor Injection

MyBatis also supports constructor injection. If Employee has the following constructor:

public Employee(String employeeId, String employeeName, Integer employeeType) {
    this.employeeId = employeeId;
    this.employeeName = employeeName;
    this.employeeType = employeeType;
}

The resultMap can be written as:

<mapper namespace="cn.felord.mybatis.mapper.EmployeeMapper">
    <resultMap id="EmployeeMap" type="cn.felord.mybatis.entity.Employee">
        <constructor>
            <idArg column="employee_id" javaType="String"/>
            <arg column="employee_name" javaType="String"/>
            <arg column="employee_type" javaType="String"/>
        </constructor>
    </resultMap>
</mapper>

If the property attribute is omitted, MyBatis injects values according to the constructor parameter order. Since MyBatis 3.4.3, the name attribute allows arguments to be out of order:

<mapper namespace="cn.felord.mybatis.mapper.EmployeeMapper">
    <resultMap id="EmployeeConstructorMap" type="cn.felord.mybatis.entity.Employee">
        <constructor>
            <idArg column="employee_id" javaType="String" name="employeeId"/>
            <!-- you can add arguments in any order -->
            <arg column="employee_type" javaType="Integer" name="employeeType"/>
            <arg column="employee_name" javaType="String" name="employeeName"/>
        </constructor>
    </resultMap>
</mapper>

2.3 Inheritance

Like Java classes, resultMap can inherit from another map. The diagram below shows two related Java classes:

The resultMap for RegularEmployee extends EmployeeMap:

<resultMap id="RegularEmployeeMap" extends="EmployeeMap" type="cn.felord.mybatis.entity.RegularEmployee">
    <result column="level" property="level"/>
    <result column="job_number" property="jobNumber"/>
    <association property="department" javaType="cn.felord.mybatis.entity.Department">
        <id column="department_id" property="departmentId"/>
        <result column="department_name" property="departmentName"/>
        <result column="department_level" property="departmentLevel"/>
    </association>
</resultMap>

2.4 One-to-One Association

The last resultMap in the inheritance example contains an association tag, which maps a RegularEmployee to a single Department object:

<resultMap id="RegularEmployeeMap" extends="EmployeeMap" type="cn.felord.mybatis.entity.RegularEmployee">
    <result column="level" property="level"/>
    <result column="job_number" property="jobNumber"/>
    <association property="department" javaType="cn.felord.mybatis.entity.Department">
        <id column="department_id" property="departmentId"/>
        <result column="department_name" property="departmentName"/>
        <result column="department_level" property="departmentLevel"/>
    </association>
</resultMap>
association can be nested further if the associated object also has one‑to‑one relationships.

2.5 One-to-Many Collection

For a department that has many employees we can map a list of employees into a DepartmentAndEmployeeList class:

public class DepartmentAndEmployeeList extends Department {
    private static final long serialVersionUID = -2503893191396554581L;
    private List<Employee> employees;
    public List<Employee> getEmployees() { return employees; }
    public void setEmployees(List<Employee> employees) { this.employees = employees; }
}

The corresponding resultMap uses the collection element:

<resultMap id="DepartmentAndEmployeeListMap" extends="DepartmentMap" type="cn.felord.mybatis.entity.DepartmentAndEmployeeList">
    <collection property="employees" ofType="cn.felord.mybatis.entity.RegularEmployee">
        <id column="employee_id" property="employeeId"/>
        <result column="employee_name" property="employeeName"/>
        <result column="level" property="level"/>
        <result column="job_number" property="jobNumber"/>
    </collection>
</resultMap>

2.6 Discriminator

When an employee can be either a regular or a temporary worker, a discriminator decides which subclass to instantiate, similar to a Java switch statement.

public class DepartmentAndTypeEmployees extends Department {
    private static final long serialVersionUID = -2503893191396554581L;
    private List<RegularEmployee> regularEmployees;
    private List<TemporaryEmployee> temporaryEmployees;
    // getters and setters
}

The resultMap with discriminators looks like:

<resultMap id="DepartmentAndTypeEmployeesMap" extends="DepartmentMap" type="cn.felord.mybatis.entity.DepartmentAndTypeEmployees">
    <collection property="regularEmployees" ofType="cn.felord.mybatis.entity.RegularEmployee">
        <discriminator javaType="int" column="employee_type">
            <case value="1">
                <id column="employee_id" property="employeeId"/>
                <result column="employee_name" property="employeeName"/>
                <result column="employee_type" property="employeeType"/>
                <result column="level" property="level"/>
                <result column="job_number" property="jobNumber"/>
            </case>
        </discriminator>
    </collection>
    <collection property="temporaryEmployees" ofType="cn.felord.mybatis.entity.TemporaryEmployee">
        <discriminator javaType="int" column="employee_type">
            <case value="0">
                <id column="employee_id" property="employeeId"/>
                <result column="employee_name" property="employeeName"/>
                <result column="employee_type" property="employeeType"/>
                <result column="company_no" property="companyNo"/>
            </case>
        </discriminator>
    </collection>
</resultMap>

Note that the collection tags must be declared first, and the discriminator placed inside each collection.

3. Summary

resultMap

satisfies most data‑mapping scenarios in MyBatis. This article covered its basic and advanced usages, including inheritance, one‑to‑one, one‑to‑many, and discriminators. While powerful, excessive nesting can affect maintainability and performance, especially with large collections. Use it wisely and refer to the official MyBatis documentation for additional attributes.

For the demo code, follow the public account and reply with resultMap to receive it.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendJavaMyBatisORMresultMap
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.