1. Introduction

The resultMap element is the most important and powerful element in MyBatis. It frees you from 90% of the JDBC ResultSets data extraction code, and in some cases allows you to do things that ARE not supported by JDBC. In fact, when writing mapping code for complex statements such as joins, a resultMap can replace thousands of lines of code that do the same thing. ResultMap is designed to achieve zero configuration for simple statements, while complex statements only need to describe the relationship between statements.

A resultMap can aggregate complex queried data, such as the data of multiple tables, one-to-one mapping, and one-to-many mapping, into a result set. Everyday business development usually deals with it. Today, resultMap will be explained in detail.

2. resultMap

Let’s take a look at how resultMap maps.

2.1 the Getter/Setter injection

We declare a database entity class:

/ * * *@author felord.cn
 * @sinceThus * were * /
@Data
public class Employee implements Serializable {
    private static final long serialVersionUID = -7145891282327539285L;
    private String employeeId;
    private String employeeName;
    private Integer employeeType;
}
Copy the code

Then its 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>
Copy the code

Let’s explain the properties of these configurations:

<mapper namespace="Globally unique namespace">
    <resultMap id="Unique under this namespace" type="Entity corresponding to mapping">
        <id column="Database primary key field name or alias, used to improve overall performance" property="Corresponding entity attributes"/>
        <result column="Database field name or alias" property="Corresponding entity attributes"/>
    </resultMap>
</mapper>
Copy the code

The above method is injected through Getter and Setter methods, that is, the entity class must be parameterless and the corresponding property must have Getter and Setter methods.

2.2 Structural injection

Getter and Setter methods for injection are the most common. But 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;
}
Copy the code

The corresponding resultMap can be written as follows:

<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>
Copy the code

If you are careful, you will notice that there is no property property, but when you do not declare a property property, you will inject it in the constructor argument list order.

Since Mybatis 3.4.3 introduced the name attribute we have been able to disarrange the arG elements in the constructor tag.

<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 it out of the argument list order -->
            <arg column="employee_type" javaType="Integer" name="employeeType"/>
            <arg column="employee_name" javaType="String" name="employeeName"/>
        </constructor>
    </resultMap>
</mapper>
Copy the code

2.3 Inheritance relationship

Like classes in Java, resultMap can be inherited. Here are two Java classes that have inheritance relationships:

RegularEmployee’s resultMap can be written as follows:

<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>
Copy the code

Extends is used for inheritance just like Java inheritance keywords.

2.4 One-to-one Association

The discerning eye will notice that the last resultMap example in 2.3 has an Association tag. What is it used for? For example, a RegularEmployee corresponds to a Department for every RegularEmployee, and there is a demand to find out such a one-to-one relationship in business. That’s where the association comes in.

<resultMap id="RegularEmployeeMap" extends="EmployeeMap" type="cn.felord.mybatis.entity.RegularEmployee">
    <result column="level" property="level"/>
    <result column="job_number" property="jobNumber"/>
    <association property="Attribute Name" javaType="Corresponding Java type">
        <id column="department_id" property="departmentId"/>
        <result column="department_name" property="departmentName"/>
        <result column="department_level" property="departmentLevel"/>
    </association>
</resultMap>
Copy the code

Associations can continue to be nested, with one-to-one relationships among possible objects.

2.5 One-to-many Association

If you have a one-to-one relationship, you have a one-to-many relationship. We going, one department has more than one employee, we may need to query a department of information and information all employees loaded into DepartmentAndEmployeeList.

/ * * *@author felord.cn
 * @since15:33 the * * /
public class DepartmentAndEmployeeList extends Department {
    private static final long serialVersionUID = -2503893191396554581L;
    private List<Employee> employees;

    public List<Employee> getEmployees(a) {
        return employees;
    }

    public void setEmployees(List<Employee> employees) {
        this.employees = employees; }}Copy the code

We can use the collection keyword in resultMap to handle one-to-many mappings:

<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>
Copy the code

2.6 discriminator

As you all know, not all employees are regular workers, there are temporary workers. Sometimes we want to be able to separate the two, and you know why. I won’t go into that. For this requirement, our mapping relationship is more complicated. We need to judge which data is regular and which data is temporary according to a certain condition, and then load them into regularEmployees and temporaryEmployees of the following entity class respectively.

/ * * *@author felord.cn
 * @since15:33 the * * /
public class DepartmentAndTypeEmployees extends Department {
    private static final long serialVersionUID = -2503893191396554581L;
    private List<RegularEmployee> regularEmployees;
    private List<TemporaryEmployee> temporaryEmployees;
    // getter setter
}
Copy the code

The discriminator element is designed to handle this, as well as other situations, such as the inheritance hierarchy of a class. The concept of a discriminator is easy to understand — it’s a lot like the Switch statement in the Java language.

To do this, we need to add an employeeType attribute of type INT to the Employee class to distinguish between regular and temporary workers, where 1 represents regular and 0 represents temporary workers. Then we to write queries DepartmentAndTypeEmployees resultMap:

<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>
Copy the code

Be sure to keep in mind is definitely a statement DepartmentAndTypeEmployees two List, and then use the discriminator tags within the collection label.

It is easy to make the following mistakes. The following method can query the data but does not meet the above requirements:

<resultMap id="DepartmentAndTypeEmployeesMap" extends="DepartmentMap"
               type="cn.felord.mybatis.entity.DepartmentAndTypeEmployees">
        <discriminator javaType="int" column="employee_type">
            <case value="1">
                <collection property="regularEmployees" ofType="cn.felord.mybatis.entity.RegularEmployee">
                    <! - omitted - >
                </collection>
            </case>
            <case value="0">
                <collection property="temporaryEmployees" ofType="cn.felord.mybatis.entity.TemporaryEmployee">
                    <! - omitted - >
                </collection>
            </case>
        </discriminator>
    </resultMap>
Copy the code

When employee_Type =1 is found, we will create a List

and place the RegularEmployee in it. We will create a List

every time. The same is true when employee_type=0. So in the end it will return a List < DepartmentAndTypeEmployees >.

3. Summary

ResultMap can meet the requirements of most business scenarios for data mapping. Today, we explain some usage of resultMap in Mybatis. In fact, resultMap has some useful attributes, which will not be explained here due to the length. You can read the official documentation of Mybatis. Note that although resultMap is powerful, you must use it properly. Complex cascades may affect maintenance and performance. For example, in one-to-many mapping, if the number of data items is too large, memory consumption and read/write performance will be increased. I hope today’s article is helpful for you to use resultMap, more timely technical information, please pay more attention to: code farmers xiao Pangge.

The DEMO of this article can be obtained by following the public account: Felordcn reply resultMap.

Follow our public id: Felordcn for more information

Personal blog: https://felord.cn