Mybatis Powerful Result Set Mapper resultMap

1. Preface

The resultMap element is the most important and powerful element in MyBatis.It frees you from 90% of JDBC ResultSets data extraction code and allows you to perform operations that JDBC does not support in some cases.In fact, when writing mapping code for complex statements such as connections, a resultMap can replace thousands of lines of code that implement the same functionality.ResultMap is designed to be zero-configured for simple statements and to be more complex, it simply describes the relationships between the statements.

ReultMap aggregates complex data queried, such as data from multiple tables, one-to-one mapping, one-to-many mapping, and so on, into a result set.Business development on a daily basis usually deals with it, and today we'll give a more detailed explanation of resultMap.

2. resultMap

Next, let's see how resultMap maps.

2.1 Getter/Setter Injection

We declare an entity class corresponding to a database:

/**
 * @author felord.cn
 * @since 16:50
 **/
@Data
public class Employee implements Serializable {
    private static final long serialVersionUID = -7145891282327539285L;
    private String employeeId;
    private String employeeName;
    private Integer employeeType;
}

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>

Let's explain the properties of these configurations:

<mapper namespace="Globally unique namespace">
    <resultMap id="book namespace Lower Unique" type="Entities corresponding to the mapping">
        <id column="Database primary key field name or alias to improve overall performance" property="Corresponding entity attributes"/>
        <result column="Database field name or alias" property="Corresponding entity attributes"/>
    </resultMap>
</mapper>

The above method is injected through the Getter and Setter methods, that is, entity classes must have parametric constructs, and corresponding attributes must have Getter and Setter methods.

2.2 Construction Injection

Getter and Setter methods are the most common ways to inject.However, Mybatis also supports construct injection if Employee has the following construct methods:

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

The corresponding resultMap can then 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>

Careful students find that there is no property attribute here, in fact, when you do not declare the property attribute, it will be injected in the order of the parameter list of the construction method.

By introducing the name attribute in Mybatis 3.4.3, we can disrupt the order of the arg elements within 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 them out of parameter list order-->
            <arg column="employee_type" javaType="Integer" name="employeeType"/>
            <arg column="employee_name" javaType="String" name="employeeName"/>
        </constructor>
    </resultMap>
</mapper>

2.3 Inheritance

Like classes in Java, resultMap is inheritable.Here are two Java classes that have an inheritance relationship:

Then RegularEmployee's resultMap would be like this:

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

Use extends for inheritance just like Java's inheritance keywords.

2.4 One-to-One Association

A clear-eyed person will see that there is an association tag in the last resultMap example of 2.3.What is this for?For example, RegularEmployee corresponds to a Department where there is a need to query this one-to-one relationship.So associations come in handy.

<resultMap id="RegularEmployeeMap" extends="EmployeeMap" type="cn.felord.mybatis.entity.RegularEmployee">
    <result column="level" property="level"/>
    <result column="job_number" property="jobNumber"/>
    <association property="Property 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>

Associations can continue to be nested, with one-to-one relationships among the objects that may be associated.

2.5 One-to-Many Association

If there is a one-to-one association, there will naturally be a one-to-many association.We are mainly anti-customer. There are many employees in a department. We may need to inquire about one department and all the employees'information to load into the DepartmentAndEmployeeList.

/**
 * @author felord.cn
 * @since 15:33
 **/
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;
    }
}

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

<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

As we all know, not all employees are regular workers, but temporary workers.Sometimes we also want to be able to distinguish the two, and you know the reason.I won't go into this problem any further.Our mapping relationship is complicated in terms of this requirement. We need to decide which data is a regular worker and which is a temporary worker based on certain criteria, then load it into regular Employees and temporary Employees of this entity class below.

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

The discriminator element is designed to handle this situation, as well as other situations, such as the inheritance hierarchy of a class.The concept of discriminator is easy to understand -- it's much like a 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 workers and 0 represents temporary workers.Then let's write a resultMap that queries DepartmentAndTypeEmployees:

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

Keep in mind that you must first declare two lists of DepartmentAndTypeEmployees, then use the discriminator tag inside the collection tag.

It is easy to make the following mistakes here. The following writings can query the data but do 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">
                    <!--ellipsis-->
                </collection>
            </case>
            <case value="0">
                <collection property="temporaryEmployees" ofType="cn.felord.mybatis.entity.TemporaryEmployee">
                    <!--ellipsis-->
                </collection>
            </case>
        </discriminator>
    </resultMap>

This means when employee_is found in this dataWhen type=1, a new List <RegularEmployee> is created and the data is put in. Each time, a new List <RegularEmployee> is created; when employee_The same is true when type = 0.This will eventually return a List <DepartmentAndTypeEmployees>.

3. Summary

ResultMap can meet the needs of most business scenarios for data mapping. Today we have explained some uses of resultMap in Mybatis. In fact, resultMap has some useful properties. For reasons of length, we will not explain it here anymore. You can read the official Mybatis documentation.Note, however, that although resultMap is powerful, it must be used appropriately, and that cascading is too complex can affect later maintenance and performance.For example, when mapping one-to-many, if the number of data bars is too large for one-to-many side, it will increase memory consumption and read-write performance.Hopefully, today's article will help you with resultMap, and more timely technical information please pay more attention to: Little Fatty Man, Mainong.

For this article, you can look at the public number: Felordcn replies to resultMap.

Focus on Public Number: Felordcn for more information

Personal blog:https://felord.cn

Tags: Programming Mybatis Java Attribute Database

Posted on Sat, 23 May 2020 17:54:21 -0700 by Julian Pedley