MyBatis中的N+1问题,使用ResultSet来解决,需要存储过程【非常详细】

参考
https://mybatis.org/mybatis-3/zh/sqlmap-xml.html
https://mybatis.net.cn/sqlmap-xml.html#Result_Maps

基础表sql

订单表

CREATE TABLE `test_order` (
  `order_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '订单id',
  `order_name` varchar(255) NOT NULL DEFAULT '' COMMENT '订单名字',
  PRIMARY KEY (`order_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COMMENT='订单表';

INSERT INTO test_order (`order_id`, `order_name`) VALUES (1, '订单1');
INSERT INTO test_order (`order_id`, `order_name`) VALUES (2, '订单2');

支付表

CREATE TABLE `test_pay` (
  `pay_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '支付id',
  `pay_name` varchar(255) NOT NULL DEFAULT '' COMMENT '支付名字',
  `order_id` bigint(20) NOT NULL COMMENT '订单id',
  PRIMARY KEY (`pay_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COMMENT='付款记录表';

INSERT INTO test_pay (`pay_id`, `pay_name`, `order_id`) VALUES (1, '支付名字1', 1);
INSERT INTO test_pay (`pay_id`, `pay_name`, `order_id`) VALUES (2, '支付名字2', 2);

物流表(一个订单有多个阶段的物流信息)

CREATE TABLE `test_flow` (
  `flow_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '物流id',
  `flow_begin_name` varchar(255) NOT NULL DEFAULT '' COMMENT '物流开始名字',
  `flow_end_name` varchar(255) NOT NULL DEFAULT '' COMMENT '物流结束名字',
  `order_id` bigint(20) NOT NULL COMMENT '订单id',
  PRIMARY KEY (`flow_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COMMENT='物流表';

INSERT INTO test_flow (`flow_id`, `flow_begin_name`, `flow_end_name`, `order_id`) VALUES (1, '北京', '上海', 1);
INSERT INTO test_flow (`flow_id`, `flow_begin_name`, `flow_end_name`, `order_id`) VALUES (2, '上海', '浦东新区', 1);
INSERT INTO test_flow (`flow_id`, `flow_begin_name`, `flow_end_name`, `order_id`) VALUES (3, '浦东新区', '川沙新政', 1);
INSERT INTO test_flow (`flow_id`, `flow_begin_name`, `flow_end_name`, `order_id`) VALUES (4, '西藏', '黑龙江', 2);
INSERT INTO test_flow (`flow_id`, `flow_begin_name`, `flow_end_name`, `order_id`) VALUES (5, '黑龙江', '漠河', 2);
INSERT INTO test_flow (`flow_id`, `flow_begin_name`, `flow_end_name`, `order_id`) VALUES (6, '漠河', '百合路35号', 2);

1个订单对应1个支付信息
1个订单对应n个阶段的物流信息
在这里插入图片描述

存储过程

DELIMITER $$

CREATE PROCEDURE `selectOrderAndFlow`(IN `orderId` bigint)
BEGIN

select order_id, order_name from test_order ;
select order_id, pay_id, pay_name from test_pay ;
select order_id, flow_id, flow_begin_name, flow_end_name from test_flow ;

END $$

java 文件

vo文件

package cn.fox.mydemo.domain;

import lombok.Data;

@Data
public class TestOrder {

    private Long orderId;

    private String orderName;

}
package cn.fox.mydemo.domain;

import lombok.Data;

@Data
public class TestPay {
    /**
     * 支付id
     */
    private Long payId;

    /**
     * 支付名字
     */
    private String payName;

    /**
     * 订单id
     */
    private Long orderId;

}
package cn.fox.mydemo.domain;

import lombok.Data;

@Data
public class TestFlow {
    private static final long serialVersionUID = 1L;

    /**
     * 物流id
     */
    private Long flowId;

    /**
     * 物流开始名字
     */
    private String flowBeginName;

    /**
     * 物流结束名字
     */
    private String flowEndName;

    /**
     * 订单id
     */
    private Long orderId;

}

package cn.fox.mydemo.domain.vo;

import cn.fox.mydemo.domain.TestFlow;
import cn.fox.mydemo.domain.TestOrder;
import cn.fox.mydemo.domain.TestPay;
import lombok.Data;

import java.util.List;

@Data
public class TestOrderVo2 {

    /** 订单对象 */
    private TestOrder testOrder;

    /** 支付对象 */
    private TestPay testPay;

    /** 物流list */
    private List<TestFlow> testFlowList;

}

mapper.java文件

package cn.fox.mydemo.mapper;

import cn.fox.mydemo.domain.vo.TestOrderVo2;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface TestOrderMapper {

    List<TestOrderVo2> getOrderAndFlow(Long id);

}

mapper.xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.fox.mydemo.mapper.TestOrderMapper">

    <resultMap type="cn.fox.mydemo.domain.vo.TestOrderVo2" id="testOrderVo2">
        <result property="testOrder.orderId"    column="order_id"    />
        <result property="testOrder.orderName"    column="order_name"    />

        <association property="testPay"
                     javaType="cn.fox.mydemo.domain.TestPay"
                     resultSet="testPay"
                     column="order_id"
                     foreignColumn="order_id">
            <id property="payId" column="pay_id"/>
            <result property="payName" column="pay_name"/>
            <result property="orderId" column="order_id"/>
        </association>

        <collection property="testFlowList"
                    ofType="cn.fox.mydemo.domain.TestFlow"
                    resultSet="testFlowList"
                    column="order_id"
                    foreignColumn="order_id">
            <id property="flowId" column="flow_id"/>
            <result property="flowBeginName" column="flow_begin_name"/>
        </collection>

    </resultMap>

    <select id="getOrderAndFlow"
            resultSets="testOrder,testPay,testFlowList"
            statementType="CALLABLE"
            resultMap="testOrderVo2">
        {call selectOrderAndFlow(#{id,jdbcType=INTEGER,mode=IN})}
    </select>

</mapper>

测试文件

package cn.fox.mydemo.mybatis;

import cn.fox.mydemo.domain.vo.TestOrderVo2;
import cn.fox.mydemo.mapper.TestOrderMapper;
import com.alibaba.fastjson.JSONObject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;
import java.util.List;

@SpringBootTest
@RunWith(SpringRunner.class)
public class MybatisDemo2 {

    @Resource
    private TestOrderMapper testOrderMapper;

    @Test
    public void testMybatis() {
        List<TestOrderVo2> orderAndFlow = testOrderMapper.getOrderAndFlow(3L);

        System.out.println(JSONObject.toJSONString(orderAndFlow));

    }

}

测试结果

一次性执行3条sql,并且根据关联字段进行复杂关系映射


Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@48a46b0f] was not registered for synchronization because synchronization is not active
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@60c96eb4] will not be managed by Spring
==>  Preparing: {call selectOrderAndFlow(?)}
==> Parameters: 3(Long)
<==    Columns: order_id, order_name
<==        Row: 1, 订单1
<==        Row: 2, 订单2
<==      Total: 2
<==    Columns: order_id, pay_id, pay_name
<==        Row: 1, 1, 支付名字1
<==        Row: 2, 2, 支付名字2
<==      Total: 2
<==    Columns: order_id, flow_id, flow_begin_name, flow_end_name
<==        Row: 1, 1, 北京, 上海
<==        Row: 1, 2, 上海, 浦东新区
<==        Row: 1, 3, 浦东新区, 川沙新政
<==        Row: 2, 4, 西藏, 黑龙江
<==        Row: 2, 5, 黑龙江, 漠河
<==        Row: 2, 6, 漠河, 百合路35号
<==      Total: 6
<==    Updates: 0
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@48a46b0f]

结果集自动映射

[
    {
        "testFlowList":[
            {
                "flowBeginName":"北京",
                "flowEndName":"上海",
                "flowId":1,
                "orderId":1
            },
            {
                "flowBeginName":"上海",
                "flowEndName":"浦东新区",
                "flowId":2,
                "orderId":1
            },
            {
                "flowBeginName":"浦东新区",
                "flowEndName":"川沙新政",
                "flowId":3,
                "orderId":1
            }
        ],
        "testOrder":{
            "orderId":1,
            "orderName":"订单1"
        },
        "testPay":{
            "orderId":1,
            "payId":1,
            "payName":"支付名字1"
        }
    },
    {
        "testFlowList":[
            {
                "flowBeginName":"西藏",
                "flowEndName":"黑龙江",
                "flowId":4,
                "orderId":2
            },
            {
                "flowBeginName":"黑龙江",
                "flowEndName":"漠河",
                "flowId":5,
                "orderId":2
            },
            {
                "flowBeginName":"漠河",
                "flowEndName":"百合路35号",
                "flowId":6,
                "orderId":2
            }
        ],
        "testOrder":{
            "orderId":2,
            "orderName":"订单2"
        },
        "testPay":{
            "orderId":2,
            "payId":2,
            "payName":"支付名字2"
        }
    }
]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用 MyBatis、PageHelper 和达梦数据库时,确保以下几点可以帮助您避免查询结果出现问题: 1. 配置 MyBatis:确保正确配置 MyBatis 的连接信息和数据库驱动,包括数据库的 URL、用户名和密码等。 2. 引入 PageHelper 插件:在 MyBatis 的配置文件,配置 PageHelper 插件以支持分页查询。例如,在 `<plugins>` 标签添加 `<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>`。 3. 配置分页参数:在进行分页查询时,确保正确设置分页参数。可以通过在查询语句直接传递 Page 对象,或者使用 PageHelper 的静态方法设置分页参数。 4. 注意数据库方言:达梦数据库可能有自己的方言特性,需要MyBatis 的配置文件正确指定方言类型。例如,在 `<properties>` 标签添加 `<property name="dialect" value="dm"></property>`。 5. 分页语句写法:MyBatis 和 PageHelper 对于不同数据库的分页语句写法有所不同。针对达梦数据库,可以使用类似 `SELECT * FROM (SELECT ROW_NUMBER() OVER() AS rn, t.* FROM table t) t WHERE t.rn BETWEEN ? AND ?` 的语句进行分页查询。 6. 注意事务处理:如果涉及到事务操作,确保在需要的地方正确开启事务,并在适当的位置提交或回滚事务。 7. 版本兼容性:确保 MyBatis、PageHelper 和达梦数据库的版本兼容性。查阅官方文档,了解各个组件之间的兼容性要求。 除了上述注意事项,还要根据具体的业务场景和需求,适当优化查询语句、索引等,以提高查询效率和避免潜在的问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值