(7) MyBatis from entry to soil-fancy query

This is the seventh chapter in mybatis series. If you haven't read the previous suggestions, first go to the [Java Tsukuba Fox] public account to view the previous text, which is convenient for understanding and grasping. In the previous article, we talked about some methods of obtaining primary keys. In this article, we will introduce some query methods in more depth, especially multi-table query.

Before starting, start preparations such as building a database and a table.

Build a database and build a table

Create a db: mybatisdemo

4 tables:

  • user (user table)
  • goods (product list)
  • orders (order table)
  • order_detail (order detail table)

The relationship between the tables:

  • There is a one-to-one relationship between orders and user, and one order is associated with one user record
  • Orders and order_detail are a one-to-many relationship, each order may contain multiple sub-orders, and each sub-order corresponds to a product

The specific table building statement is as follows:

DROP DATABASE IF EXISTS `mybatisdemo`;
CREATE DATABASE `mybatisdemo`;
USE `mybatisdemo`;
DROP TABLE IF EXISTS user;
CREATE TABLE user(
  id int AUTO_INCREMENT PRIMARY KEY COMMENT '用户id',
  name VARCHAR(32) NOT NULL DEFAULT '' COMMENT '用户名'
) COMMENT '用户表';
INSERT INTO user VALUES (1,'冢狐'),(2,'Java冢狐');
DROP TABLE IF EXISTS goods;
CREATE TABLE goods(
  id int AUTO_INCREMENT PRIMARY KEY COMMENT '商品id',
  name VARCHAR(32) NOT NULL DEFAULT '' COMMENT '商品名称',
  price DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT '商品价格'
) COMMENT '商品信息表';
INSERT INTO goods VALUES (1,'Mybatis系列',8.00),(2,'spring系列',16.00);
DROP TABLE IF EXISTS orders;
CREATE TABLE orders(
  id int AUTO_INCREMENT PRIMARY KEY COMMENT '订单id',
  user_id INT NOT NULL DEFAULT 0 COMMENT '用户id,来源于user.id',
  create_time BIGINT NOT NULL DEFAULT 0 COMMENT '订单创建时间(时间戳,秒)',
  up_time BIGINT NOT NULL DEFAULT 0 COMMENT '订单最后修改时间(时间戳,秒)'
) COMMENT '订单表';
INSERT INTO orders VALUES (1,2,unix_timestamp(now()),unix_timestamp(now())),(2,1,unix_timestamp(now()),unix_timestamp(now()));
DROP TABLE IF EXISTS order_detail;
CREATE TABLE order_detail(
  id int AUTO_INCREMENT PRIMARY KEY COMMENT '订单明细id',
  order_id INT NOT NULL DEFAULT 0 COMMENT '订单id,来源于order.id',
  goods_id INT NOT NULL DEFAULT 0 COMMENT '商品id,来源于goods.id',
  num INT NOT NULL DEFAULT 0 COMMENT '商品数量',
  total_price DECIMAL(12,2) NOT NULL DEFAULT 0 COMMENT '商品总金额'
) COMMENT '订单表';
INSERT INTO order_detail VALUES (1,1,1,2,16.00),(2,1,1,1,16.00),(3,2,1,1,8.00);
select * from user;
select * from goods;
select * from orders;
select * from order_detail;

After building the database and tables, we will start our query journey in the next step, starting with the most basic single-table query, and then introducing single-table query, one-to-one query and one-to-many query in turn.

Single table query (3 ways)

The first introduction is the single table query.

demand

The order information needs to be queried according to the order id.

Way 1

Create a Model corresponding to each table

The fields of the table in db are separated by underscores. In the model, we use the camel nomenclature to name, such as OrderModel:

package com.zhonghu.chat07.demo1.model;
import lombok.*;
import java.util.List;
@Getter
@Setter
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class OrderModel {
    private Integer id;
    private Integer userId;
    private Long createTime;
    private Long upTime;
}

Several other models are similar.

Mapper xml
<select id="getById" resultType="com.zhonghu.chat07.demo1.model.OrderModel">
    <![CDATA[
    SELECT a.id,a.user_id as userId,a.create_time createTime,a.up_time upTime FROM orders a WHERE a.id = #{value}
    ]]>
</select>

Note the resultType above, which identifies the type of result.

Mapper interface method
OrderModel getById(int id);
mybatis global configuration file
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 引入外部jdbc配置 -->
    <properties resource="jdbc.properties"/>
    <!-- 环境配置,可以配置多个环境 -->
    <environments default="demo4">
        <environment id="demo4">
            <!-- 事务管理器工厂配置 -->
            <transactionManager type="JDBC"/>
            <!-- 数据源工厂配置,使用工厂来创建数据源 -->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="demo1/mapper/UserMapper.xml" />
        <mapper resource="demo1/mapper/GoodsMapper.xml" />
        <mapper resource="demo1/mapper/OrderMapper.xml" />
        <mapper resource="demo1/mapper/OrderDetailMapper.xml" />
    </mappers>
</configuration>
Test case
com.zhonghu.chat07.demo1.Demo1Test#getById
@Before
public void before() throws IOException {
    //指定mybatis全局配置文件
    String resource = "demo1/mybatis-config.xml";
    //读取全局配置文件
    InputStream inputStream = Resources.getResourceAsStream(resource);
    //构建SqlSessionFactory对象
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    this.sqlSessionFactory = sqlSessionFactory;
}
@Test
public void getById() {
    try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
        OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
        OrderModel orderModel = mapper.getById(1);
        log.info("{}", orderModel);
    }
}
Run output
35:59.211 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById - ==>  Preparing: SELECT a.id,a.user_id as userId,a.create_time createTime,a.up_time upTime FROM orders a WHERE a.id = ? 
35:59.239 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById - ==> Parameters: 1(Integer)
35:59.258 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById - <==      Total: 1
35:59.258 [main] INFO  c.j.chat05.demo1.Demo1Test - OrderModel(id=1, userId=2, createTime=1610803573, upTime=1610803573)
principle

In sql, we used aliases to convert the fields in orders into the same names as the fields in OrderModel. Finally, mybatis internally will reflect the query results to find fields with the same names in OrderModel according to the names, and then assign them.

Way 2

If the fields in the Model corresponding to the table in our project all use the camel naming method, some configurations can be made in mybatis to automatically map the fields in the table with the fields corresponding to the camel naming method in the Model.

The following configuration needs to be added to the mybatis global configuration file:

<settings>
    <!-- 是否开启自动驼峰命名规则映射,及从xx_yy映射到xxYy -->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
Mapper xml
<select id="getById1" resultType="com.zhonghu.chat07.demo1.model.OrderModel">
    <![CDATA[
    SELECT a.id,a.user_id,a.create_time,a.up_time FROM orders a WHERE a.id = #{value}
    ]]>
</select>

Note that in the above sql, we did not write aliases. Since we have enabled automatic camel naming mapping, the query results will be automatically mapped according to the following relationship:

sql corresponding field Fields in OrderModel
id id
user_id userId
create_time createTime
up_time upTime
Mapper interface
OrderModel getById1(int id);
Test case
com.zhonghu.chat05.demo1.Demo1Test#getById1
@Test
public void getById1() {
    try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
        OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
        OrderModel orderModel = mapper.getById1(1);
        log.info("{}", orderModel);
    }
}
Run output
59:44.884 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - ==>  Preparing: SELECT a.id,a.user_id,a.create_time,a.up_time FROM orders a WHERE a.id = ? 
59:44.917 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - ==> Parameters: 1(Integer)
59:44.935 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - <==      Total: 1
59:44.935 [main] INFO  c.j.chat05.demo1.Demo1Test - OrderModel(id=1, userId=2, createTime=1610803573, upTime=1610803573)

It can be seen from the output that the fields in sql are underlined, and the fields in OrderModel are camel naming, and the result is automatically assembled. This is the effect of turning on mapUnderscoreToCamelCase.

Way 3

There is a more powerful element resultMap in mapper xml, through which the mapping relationship of query results can be defined.

Mapper xml
<resultMap id="orderModelMap2" type="com.zhonghu.chat07.demo1.model.OrderModel">
    <id column="id" property="id" />
    <result column="user_id" property="userId" />
    <result column="create_time" property="createTime" />
    <result column="up_time" property="upTime" />
</resultMap>
<select id="getById2" resultMap="orderModelMap2">
    <![CDATA[
    SELECT a.id,a.user_id,a.create_time,a.up_time FROM orders a WHERE a.id = #{value}
    ]]>
</select>

The above resultMap has 2 elements that need to be specified:

  • id: resultMap identification
  • type: What type to encapsulate the result, here we need to package the result as OrderModel

Note that in the select element above, there is a resultMap that identifies which resultMap is used for mapping the query results. Here we use orderModelMap2, so the query results will be mapped according to the resultMap associated with orderModelMap2.

Mapper interface
OrderModel getById2(int id);
Test case
com.zhonghu.chat07.demo1.Demo1Test#getById2
@Test
public void getById2() {
    try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
        OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
        OrderModel orderModel = mapper.getById2(1);
        log.info("{}", orderModel);
    }
}

Run output

14:12.518 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById2 - ==>  Preparing: SELECT a.id,a.user_id,a.create_time,a.up_time FROM orders a WHERE a.id = ? 
14:12.546 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById2 - ==> Parameters: 1(Integer)
14:12.564 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById2 - <==      Total: 1
14:12.564 [main] INFO  c.j.chat05.demo1.Demo1Test - OrderModel(id=1, userId=2, createTime=1610803573, upTime=1610803573)

One-to-one related query (4 ways)

After talking about the single-table query, let’s start with the table query, the first is the one-to-one query

demand

When querying an order by order id, the user information associated with the order is also returned.

Let's modify the OrderModel code and add a UserModel inside, as follows:

package com.zhonghu.chat07.demo2.model;
import lombok.*;
import java.util.List;
@Getter
@Setter
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class OrderModel {
    private Integer id;
    private Integer userId;
    private Long createTime;
    private Long upTime;
    //下单用户信息
    private UserModel userModel;
}

UserModel content:

package com.zhonghu.chat07.demo2.model;
import lombok.*;
@Getter
@Setter
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class UserModel {
    private Integer id;
    private String name;
}

Way 1

Mapper xml
<resultMap id="orderModelMap1" type="com.zhonghu.chat07.demo2.model.OrderModel">
    <id column="id" property="id" />
    <result column="user_id" property="userId"/>
    <result column="create_time" property="createTime"/>
    <result column="up_time" property="upTime"/>
    <result column="user_id" property="userModel.id"/>
    <result column="name" property="userModel.name"/>
</resultMap>
<select id="getById1" resultMap="orderModelMap1">
    <![CDATA[
    SELECT
        a.id,
        a.user_id,
        a.create_time,
        a.up_time,
        b.name
    FROM
        orders a,
        user b
    WHERE
        a.user_id = b.id
    AND a.id = #{value}
    ]]>
</select>

Pay attention to the two lines above:

<result column="user_id" property="userModel.id"/>
<result column="name" property="userModel.name"/>

In this place, cascade assignment is used, and. Is used for reference between multiple levels. Here we only have one level, and there can be many levels.

Mapper interface
OrderModel getById1(int id);
Test case
com.zhonghu.chat07.demo2.Demo2Test#getById1
@Before
public void before() throws IOException {
    //指定mybatis全局配置文件
    String resource = "demo2/mybatis-config.xml";
    //读取全局配置文件
    InputStream inputStream = Resources.getResourceAsStream(resource);
    //构建SqlSessionFactory对象
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    this.sqlSessionFactory = sqlSessionFactory;
}
@Test
public void getById1() {
    try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
        OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
        OrderModel orderModel = mapper.getById1(1);
        log.info("{}", orderModel);
    }
}
Run output
24:20.811 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - ==>  Preparing: SELECT a.id, a.user_id, a.create_time, a.up_time, b.name FROM orders a, user b WHERE a.user_id = b.id AND a.id = ? 
24:20.843 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - ==> Parameters: 1(Integer)
24:20.861 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - <==      Total: 1
24:20.861 [main] INFO  c.j.chat05.demo2.Demo2Test - OrderModel(id=1, userId=2, createTime=1610803573, upTime=1610803573, userModel=UserModel(id=2, name=Java冢狐))

Way 2

This time we need to use another element association in mapper xml, this element can configure the mapping relationship of the associated objects, see the example.

Mapper xml
<resultMap id="orderModelMap2" type="com.zhonghu.chat07.demo2.model.OrderModel">
    <id column="id" property="id" />
    <result column="user_id" property="userId"/>
    <result column="create_time" property="createTime"/>
    <result column="up_time" property="upTime"/>
    <association property="userModel">
        <id column="user_id" property="id"/>
        <result column="name" property="name" />
    </association>
</resultMap>
<select id="getById2" resultMap="orderModelMap2">
    <![CDATA[
    SELECT
        a.id,
        a.user_id,
        a.create_time,
        a.up_time,
        b.name
    FROM
        orders a,
        user b
    WHERE
        a.user_id = b.id
    AND a.id = #{value}
    ]]>
</select>

Note the following part of the code above:

<association property="userModel">
    <id column="user_id" property="id"/>
    <result column="name" property="name" />
</association>

Pay attention to the property attribute above. This is to configure the mapping relationship between the sql query result and the OrderModel.userModel object, and map the user_id to the id in the userModel, and the name to the name in the userModel.

Mapper interface
OrderModel getById2(int id);
Test case
@Test
public void getById2() {
    try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
        OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
        OrderModel orderModel = mapper.getById2(1);
        log.info("{}", orderModel);
    }
}
operation result
51:44.896 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById2 - ==>  Preparing: SELECT a.id, a.user_id, a.create_time, a.up_time, b.name FROM orders a, user b WHERE a.user_id = b.id AND a.id = ? 
51:44.925 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById2 - ==> Parameters: 1(Integer)
51:44.941 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById2 - <==      Total: 1
51:44.942 [main] INFO  c.j.chat05.demo2.Demo2Test - OrderModel(id=1, userId=2, createTime=1610803573, upTime=1610803573, userModel=UserModel(id=2, name=Java冢狐))

It can be seen from the last line of the result that the value mapping of all fields is ok.

Way 3

First query the order data according to the order id, and then go to the user table to query the user data through the user_id in the order. After two queries, the target result is combined. Mybatis has built-in this kind of operation, as follows.

UserMapper.xml

We first define a select element to query user information by user id, as follows

<select id="getById" resultType="com.zhonghu.chat07.demo2.model.UserModel">
    <![CDATA[
    SELECT id,name FROM user where id = #{value}
    ]]>
</select>
OrderModel.xml
<resultMap id="orderModelMap3" type="com.zhonghu.chat07.demo2.model.OrderModel">
    <id column="id" property="id" />
    <result column="user_id" property="userId"/>
    <result column="create_time" property="createTime"/>
    <result column="up_time" property="upTime"/>
    <association property="userModel" select="com.zhonghu.chat07.demo2.mapper.UserMapper.getById" column="user_id" />
</resultMap>
<select id="getById3" resultMap="orderModelMap3">
    <![CDATA[
    SELECT
        a.id,
        a.user_id,
        a.create_time,
        a.up_time
    FROM
        orders a
    WHERE
        a.id = #{value}
    ]]>
</select>

The value of the OrderModel.userModel attribute comes from another query, this query is specified by the select attribute of the association element, here is

com.zhonghu.chat07.demo2.mapper.UserMapper.getById

This query is conditional, and the condition is passed through the column of association, where the user_id field in the query result of getById3 is passed.

Mapper interface
OrderModel getById3(int id);
Test case
com.zhonghu.chat07.demo2.Demo2Test#getById3
@Test
public void getById3() {
    try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
        OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
        OrderModel orderModel = mapper.getById3(1);
        log.info("{}", orderModel);
    }
}
Run output
07:12.569 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById3 - ==>  Preparing: SELECT a.id, a.user_id, a.create_time, a.up_time FROM orders a WHERE a.id = ? 
07:12.600 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById3 - ==> Parameters: 1(Integer)
07:12.619 [main] DEBUG c.j.c.d.mapper.UserMapper.getById - ====>  Preparing: SELECT id,name FROM user where id = ? 
07:12.620 [main] DEBUG c.j.c.d.mapper.UserMapper.getById - ====> Parameters: 2(Integer)
07:12.625 [main] DEBUG c.j.c.d.mapper.UserMapper.getById - <====      Total: 1
07:12.625 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById3 - <==      Total: 1
07:12.625 [main] INFO  c.j.chat05.demo2.Demo2Test - OrderModel(id=1, userId=2, createTime=1610803573, upTime=1610803573, userModel=UserModel(id=2, name=Java冢狐))

It can be seen from the output that there are 2 queries, first query the order according to the order id, and then go to the user table to query the user information through the user id in the order record, and finally perform 2 queries.

Way 4

In Method 3, one parameter is passed to the second query. What if you need to pass multiple parameters to the second query? Can be written like this

<association property="属性" select="查询对应的select的id" column="{key1=父查询字段1,key2=父查询字段2,key3=父查询字段3}" />

This is equivalent to passing a map to the subquery. In the subquery, the key of the map needs to be used to obtain the corresponding conditions. See the case:

OrderMapper.xml

<resultMap id="orderModelMap4" type="com.zhonghu.chat07.demo2.model.OrderModel">
    <id column="id" property="id" />
    <result column="user_id" property="userId"/>
    <result column="create_time" property="createTime"/>
    <result column="up_time" property="upTime"/>
    <association property="userModel" select="com.zhonghu.chat07.demo2.mapper.UserMapper.getById1" column="{uid1=user_id,uid2=create_time}" />
</resultMap>
<select id="getById4" resultMap="orderModelMap4">
    <![CDATA[
    SELECT
        a.id,
        a.user_id,
        a.create_time,
        a.up_time
    FROM
        orders a
    WHERE
        a.id = #{value}
    ]]>
</select>

UserMapper.xml

<select id="getById1" resultType="com.zhonghu.chat07.demo2.model.UserModel">
    <![CDATA[
    SELECT id,name FROM user where id = #{uid1} and id = #{uid2}
    ]]>
</select>

Mapper interface

OrderModel getById4(int id);

Test case

com.zhonghu.chat07.demo2.Demo2Test#getById4
@Test
public void getById4() {
    try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
        OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
        OrderModel orderModel = mapper.getById4(1);
        log.info("{}", orderModel);
    }
}

Run output

19:59.881 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById4 - ==>  Preparing: SELECT a.id, a.user_id, a.create_time, a.up_time FROM orders a WHERE a.id = ? 
19:59.914 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById4 - ==> Parameters: 1(Integer)
19:59.934 [main] DEBUG c.j.c.d.mapper.UserMapper.getById1 - ====>  Preparing: SELECT id,name FROM user where id = ? and id = ? 
19:59.934 [main] DEBUG c.j.c.d.mapper.UserMapper.getById1 - ====> Parameters: 2(Integer), 1610803573(Long)
19:59.939 [main] DEBUG c.j.c.d.mapper.UserMapper.getById1 - <====      Total: 0
19:59.939 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById4 - <==      Total: 1
19:59.939 [main] INFO  c.j.chat05.demo2.Demo2Test - OrderModel(id=1, userId=2, createTime=1610803573, upTime=1610803573, userModel=null)

Take a look at the conditions of the second query in the output. What is passed is the user_id and create_time of the first query.

One-to-many query (2 ways)

The final step is to analyze a team of multiple queries

demand

Query the order information according to the order id, and query the order detail list.

First modify the OrderModel code as follows:

package com.zhonghu.chat07.demo3.model;
import lombok.*;
import java.util.List;
@Getter
@Setter
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class OrderModel {
    private Integer id;
    private Integer userId;
    private Long createTime;
    private Long upTime;
    //订单详情列表
    private List<OrderDetailModel> orderDetailModelList;
}

A collection orderDetailModelList is added to OrderModel to store the list of order details.

Way 1

OrderMapper.xml
<resultMap id="orderModelMap1" type="com.zhonghu.chat07.demo3.model.OrderModel">
    <id column="id" property="id"/>
    <result column="user_id" property="userId"/>
    <result column="create_time" property="createTime"/>
    <result column="up_time" property="upTime"/>
    <collection property="orderDetailModelList" ofType="com.zhonghu.chat07.demo3.model.OrderDetailModel">
        <id column="orderDetailId" property="id"/>
        <result column="order_id" property="orderId"/>
        <result column="goods_id" property="goodsId"/>
        <result column="num" property="num"/>
        <result column="total_price" property="totalPrice"/>
    </collection>
</resultMap>
<select id="getById1" resultMap="orderModelMap1">
    <![CDATA[
    SELECT
        a.id ,
        a.user_id,
        a.create_time,
        a.up_time,
        b.id orderDetailId,
        b.order_id,
        b.goods_id,
        b.num,
        b.total_price
    FROM
        orders a,
        order_detail b
    WHERE
        a.id = b.order_id
        AND a.id = #{value}
    ]]>
</select>

Pay attention to the sql in getById1 above. This sql uses t_order and t_order_detail connection queries. This query will return multiple results, but the final result will be mapped according to orderModelMap1, and finally only one OrderModel object will be returned. The key lies in the collection element, this element Used to define the mapping relationship of elements in the collection, there are two attributes that need to be noted:

  • property: the corresponding property name
  • ofType: the type of elements in the collection, here is OrderDetailModel

The principle is this, note that there is a

<id column="id" property="id"/>

The query results will be grouped according to the column specified in this configuration, that is, grouped according to the order id. Each order corresponds to multiple order details. The order details will be mapped to the object specified by the ofType element according to the configuration of the collection.

The id element in the actual resultMap element can be replaced by the result element, but the id can improve performance. Mybatis can judge the only record by the value of the column configured by the id element. If we use the result element, then when judging whether it is the same record , It needs to be judged by all the columns, so the performance can be improved by id, and the id element can improve the performance in one-to-many. The performance is the same if the id element or the result element is used in a single table query.

Mapper interface
OrderModel getById1(Integer id);
Test case
com.zhonghu.chat07.demo3.Demo3Test#getById1
@Before
public void before() throws IOException {
    //指定mybatis全局配置文件
    String resource = "demo3/mybatis-config.xml";
    //读取全局配置文件
    InputStream inputStream = Resources.getResourceAsStream(resource);
    //构建SqlSessionFactory对象
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    this.sqlSessionFactory = sqlSessionFactory;
}
@Test
public void getById1() {
    try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
        OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
        Integer id = 1;
        OrderModel orderModel = mapper.getById1(id);
        log.info("{}", orderModel);
    }
}
Run output
03:52.092 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - ==>  Preparing: SELECT a.id , a.user_id, a.create_time, a.up_time, b.id orderDetailId, b.order_id, b.goods_id, b.num, b.total_price FROM orders a, order_detail b WHERE a.id = b.order_id AND a.id = ? 
03:52.124 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - ==> Parameters: 1(Integer)
03:52.148 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById1 - <==      Total: 2
03:52.148 [main] INFO  c.j.chat05.demo3.Demo3Test - OrderModel(id=1, userId=2, createTime=1610803573, upTime=1610803573, orderDetailModelList=[OrderDetailModel(id=1, orderId=1, goodsId=1, num=2, totalPrice=16.00), OrderDetailModel(id=2, orderId=1, goodsId=1, num=1, totalPrice=16.00)])

Note that the last output is consistent with the expected result.

Way 2

Through 2 queries, then the results are subpackaged, first query the order information through the order id, then query the order detail list through the order id, and then encapsulate the results. This play is supported by default in mybatis, and it is achieved through the collection element.

OrderDetailMapper.xml
<select id="getListByOrderId1" resultType="com.zhonghu.chat07.demo3.model.OrderDetailModel" parameterType="int">
    <![CDATA[
    SELECT
        a.id,
        a.order_id AS orderId,
        a.goods_id AS goodsId,
        a.num,
        a.total_price AS totalPrice
    FROM
        order_detail a
    WHERE
        a.order_id = #{value}
    ]]>
</select>
OrderMapper.xml
<resultMap id="orderModelMap2" type="com.zhonghu.chat07.demo3.model.OrderModel">
    <id column="id" property="id"/>
    <result column="user_id" property="userId"/>
    <result column="create_time" property="createTime"/>
    <result column="up_time" property="upTime"/>
    <collection property="orderDetailModelList" select="com.zhonghu.chat07.demo3.mapper.OrderDetailMapper.getListByOrderId1" column="id"/>
</resultMap>
<select id="getById2" resultMap="orderModelMap2">
    <![CDATA[
    SELECT
        a.id ,
        a.user_id,
        a.create_time,
        a.up_time
    FROM
        orders a
    WHERE
        a.id = #{value}
    ]]>
</select>

The focus is on the following configuration:

<collection property="orderDetailModelList" select="com.zhonghu.chat07.demo3.mapper.OrderDetailMapper.getListByOrderId1" column="id"/>

Indicates that the value of the orderDetailModelList attribute is obtained through the query specified by the select attribute, namely:

com.zhonghu.chat07.demo3.mapper.OrderDetailMapper.getListByOrderId1

The query parameters are specified by the column attribute. Here, the id in getById2 sql is used as the condition, that is, the order id.

Mapper interface
OrderModel getById2(int id);
Test case
com.zhonghu.chat07.demo3.Demo3Test#getById2
@Test
public void getById2() {
    try (SqlSession sqlSession = this.sqlSessionFactory.openSession(true);) {
        OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
        OrderModel orderModel = mapper.getById2(1);
        log.info("{}", orderModel);
    }
}
Run output
10:07.087 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById2 - ==>  Preparing: SELECT a.id , a.user_id, a.create_time, a.up_time FROM ordera a WHERE a.id = ? 
10:07.117 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById2 - ==> Parameters: 1(Integer)
10:07.135 [main] DEBUG c.j.c.d.m.O.getListByOrderId1 - ====>  Preparing: SELECT a.id, a.order_id AS orderId, a.goods_id AS goodsId, a.num, a.total_price AS totalPrice FROM order_detail a WHERE a.order_id = ? 
10:07.136 [main] DEBUG c.j.c.d.m.O.getListByOrderId1 - ====> Parameters: 1(Integer)
10:07.141 [main] DEBUG c.j.c.d.m.O.getListByOrderId1 - <====      Total: 2
10:07.142 [main] DEBUG c.j.c.d.mapper.OrderMapper.getById2 - <==      Total: 1
10:07.142 [main] INFO  c.j.chat05.demo3.Demo3Test - OrderModel(id=1, userId=2, createTime=1610803573, upTime=1610803573, orderDetailModelList=[OrderDetailModel(id=1, orderId=1, goodsId=1, num=2, totalPrice=16.00), OrderDetailModel(id=2, orderId=1, goodsId=1, num=1, totalPrice=16.00)])

There are 2 queries in the output. First query the order information through the order id, and then query the order details through the order id. The results are assembled internally by mybatis.

to sum up

  1. In the mybatis global configuration file, mapUnderscoreToCamelCase can open the fields in sql and the fields of camel naming in javabean for automatic mapping
  2. Master the common usage of resultMap element
  3. One-to-one association query uses resultMap->association element (2 ways)
  4. One-to-many query using resultMap->collection element (2 ways)
  5. The id element used in resultMap can improve efficiency in complex related queries. You can use this to determine the uniqueness of the record. If you don’t have this, you need to pass all the result-related columns to determine the uniqueness of the record.

Suggest

Mybatis provides us with powerful association queries, but I recommend using them as little as possible. It is best to use a single table query, through multiple queries in the program, and then assemble the results yourself.

It is best to define only some attributes associated with single-table fields in the Model, and not to be mixed with references to other objects.

At last

  • If you feel that you are rewarded after reading it, I hope to pay attention to it. By the way, give me a thumbs up. This will be the biggest motivation for my update. Thank you for your support.
  • Welcome everyone to pay attention to my public account [Java Fox], focusing on the basic knowledge of java and computer, I promise to let you get something after reading it, if you don’t believe me, hit me
  • Seek one-click triple connection: like, forward, and watching.
  • If you have different opinions or suggestions after reading, please comment and share with us. Thank you for your support and love.

——I am Chuhu, and I love programming as much as you.

Welcome to follow the public account "Java Fox" for the latest news

Guess you like

Origin blog.csdn.net/issunmingzhi/article/details/113831504