Mybatis一对多查询的两种姿势(值得收藏)
作者:臭小子帅 发布时间:2023-07-01 00:20:08
前言
最近碰到了Mybatis一对多查询的场景,在这里总结对比下常见的两种实现方式。
本文以常见的订单表和订单详情表来举例说明;
数据库表准备
订单表 tbl_order
订单详情表 tlb_order_detail
ps: 一个订单关联多个订单详情,通过order_no订单号关联;
实例演示
方法一:联合查询ResultMap映射
sql直接关联查询,然后结果集通过resultMap的collection映射
例如 查询订单列表,包括订单详情
Order.java 中新增字段orderDetailList,用于存详情列表
public class Order {
private Integer id;
private String orderNo;
private Date orderTime;
private Date payTime;
private String remark;
/**订单详情*/
private List<OrderDetail> orderDetailList;
//省略get、set
OrderMapper.java 新增查询方法
List<Order> queryOrderList(Map map);
OrderMapper.xml
<resultMap id="BaseResultMap" type="com.chouxiaozi.mybatisdruid.entity.Order" >
<id column="id" property="id" jdbcType="INTEGER" />
<result column="order_no" property="orderNo" jdbcType="VARCHAR" />
<result column="order_time" property="orderTime" jdbcType="TIMESTAMP" />
<result column="pay_time" property="payTime" jdbcType="TIMESTAMP" />
<result column="remark" property="remark" jdbcType="VARCHAR" />
<collection property="orderDetailList" ofType="com.chouxiaozi.mybatisdruid.entity.OrderDetail">
<id column="d_id" property="id" jdbcType="INTEGER" />
<result column="d_order_no" property="orderNo" jdbcType="VARCHAR" />
<result column="good_name" property="goodName" jdbcType="VARCHAR" />
<result column="good_id" property="goodId" jdbcType="INTEGER" />
<result column="good_count" property="goodCount" jdbcType="INTEGER" />
</collection>
</resultMap>
<select id="queryOrderList" resultMap="BaseResultMap">
SELECT
o.*, d.id as d_id,d.order_no as d_order_no,d.good_name,d.good_id,d.good_count
FROM
tbl_order o
LEFT JOIN tbl_order_detail d ON d.order_no = o.order_no
where 1=1
<if test="orderNo != null and orderNo != ''">
and o.order_no = #{orderNo}
</if>
ORDER BY o.order_time desc
</select>
查询结果展示
[
{
"id": 2,
"orderNo": "DD000002",
"orderTime": "2021-05-09 12:25:57",
"payTime": "2021-05-09 12:25:59",
"remark": "2号订单",
"orderDetailList": [
{
"id": 5,
"orderNo": "DD000002",
"goodName": "耳机",
"goodId": 5,
"goodCount": 1
},
{
"id": 4,
"orderNo": "DD000002",
"goodName": "手机",
"goodId": 4,
"goodCount": 1
}
]
},
{
"id": 1,
"orderNo": "DD000001",
"orderTime": "2021-05-09 12:25:37",
"payTime": "2021-05-09 12:25:41",
"remark": "1号订单",
"orderDetailList": [
{
"id": 2,
"orderNo": "DD000001",
"goodName": "饮料",
"goodId": 2,
"goodCount": 2
},
{
"id": 1,
"orderNo": "DD000001",
"goodName": "瓜子",
"goodId": 1,
"goodCount": 1
},
{
"id": 3,
"orderNo": "DD000001",
"goodName": "矿泉水",
"goodId": 3,
"goodCount": 2
}
]
}
]
原理:sql直接关联查询,然后结果集通过resultMap的collection映射,将order_detail表对应的字段映射到orderDetailList字段中。
优点:条件查询方便;无论是订单表还是详情表如果要进行一些条件过滤的话,非常方便,直接写在where中限制就行。
不足:因为是先关联查询,后映射;如果需要进行分页查询的话,这种方式就无法满足。主表2条数据,详情表5条数据,关联之后就是10条,无法得主表进行分页;解决方法,就是先给主表套个子查询limit分页后,然后结果集再跟详情表进行关联查询;
方法二:子查询映射
通过resultMap中collection标签的select属性去执行子查询
还以查询订单列表为例
OrderMapper.java
List<Order> queryOrderList2(Map map);
OrderMapper.xml
<!--主查询的resultMap-->
<resultMap id="BaseResultMap2" type="com.chouxiaozi.mybatisdruid.entity.Order" >
<id column="id" property="id" jdbcType="INTEGER" />
<result column="order_no" property="orderNo" jdbcType="VARCHAR" />
<result column="order_time" property="orderTime" jdbcType="TIMESTAMP" />
<result column="pay_time" property="payTime" jdbcType="TIMESTAMP" />
<result column="remark" property="remark" jdbcType="VARCHAR" />
<!--select子查询, column 传给子查询的参数-->
<collection property="orderDetailList" ofType="com.chouxiaozi.mybatisdruid.entity.OrderDetail"
select="queryDetail" column="order_no">
</collection>
</resultMap>
<!--主查询的sql-->
<select id="queryOrderList2" resultMap="BaseResultMap2">
SELECT
o.*
FROM
tbl_order o
where 1=1
<if test="orderNo != null and orderNo != ''">
and o.order_no = #{orderNo}
</if>
ORDER BY o.order_time desc
</select>
<!--子查询的resultMap-->
<resultMap id="detailResuleMap" type="com.chouxiaozi.mybatisdruid.entity.OrderDetail">
<id column="id" property="id" jdbcType="INTEGER" />
<result column="order_no" property="orderNo" jdbcType="VARCHAR" />
<result column="good_name" property="goodName" jdbcType="VARCHAR" />
<result column="good_id" property="goodId" jdbcType="INTEGER" />
<result column="good_count" property="goodCount" jdbcType="INTEGER" />
</resultMap>
<!--子查询的sql-->
<select id="queryDetail" resultMap="detailResuleMap">
SELECT
*
FROM
`tbl_order_detail` where order_no = #{order_no}
</select>
查询结果同上个例子一样;
原理:通过collection的select方法去调用子查询;所需参数通过column传递;
优点:无论是分页还是普通查询都能满足;主表增加过滤条件也很方便,直接在主查询的sql中增加where条件就行
缺点:子查询不好增加过滤条件;column只能传递主表已有的字段。下面提供解决方式;
ps:column传递多个参数 column=“{prop1=col1,prop2=col2}”
例如:实际场景中,详情表有个状态字段,只展示状态正常的详情,需要过滤详情记录。
本例子没有状态字段,就查询订单列表,详情中不展示瓜子,即详情记录中过滤掉good_id = 1的;
在上个例子基础上修改如下:
调用层传参
Map map = new HashMap();
map.put("goodId", 1);
orderMapper.queryOrderList2(map);
orderMapper.xml中增加传参过滤
展示结果如下:详情中已成功过滤掉瓜子;记住,过滤子查询不会影响主表记录;
[
{
"id": 2,
"orderNo": "DD000002",
"orderTime": "2021-05-09 12:25:57",
"payTime": "2021-05-09 12:25:59",
"remark": "2号订单",
"orderDetailList": [
{
"id": 4,
"orderNo": "DD000002",
"goodName": "手机",
"goodId": 4,
"goodCount": 1
},
{
"id": 5,
"orderNo": "DD000002",
"goodName": "耳机",
"goodId": 5,
"goodCount": 1
}
]
},
{
"id": 1,
"orderNo": "DD000001",
"orderTime": "2021-05-09 12:25:37",
"payTime": "2021-05-09 12:25:41",
"remark": "1号订单",
"orderDetailList": [
{
"id": 2,
"orderNo": "DD000001",
"goodName": "饮料",
"goodId": 2,
"goodCount": 2
},
{
"id": 3,
"orderNo": "DD000001",
"goodName": "矿泉水",
"goodId": 3,
"goodCount": 2
}
]
}
]
总结
方式 | 联合查询映射 | 子查询映射 |
---|---|---|
原理 | sql查询完成后再通过resultmap映射结果 | 主表的数据集循环调用子查询 |
分页 | 不支持分页查询,主表套子查询也能实现 | 支持分页 |
条件过滤 | 方便条件过滤 | 传参也能实现,复杂参数例如list不好传递给子查询 ;子查询过滤不影响主表数据 |
来源:https://blog.csdn.net/shuai8624/article/details/116563491


猜你喜欢
- 本文句句走心,希望老铁们用心阅读并实战,一定会有收获的。摘要:本文主要讨论生产环境中枚举类的使用。首先会通过对枚举类概念进行简单的介绍,引入
- 首先需要有网络权限,然后我们这里匹配的网络请求是之前封装好的Okhttp。非常的简单方便,直接复制进去,依赖一下包,然后调用方法即可。 这里
- 方法的返回值为什么要有带返回值的方法呢?调用处拿到方法的结果之后,才能根据结果进行下一步操作带返回值方法的定义和调用:如果在调用处,要根据方
- 本文实例讲述了Android保持屏幕常亮唤醒状态的方法。分享给大家供大家参考,具体如下:第一步: 首先添加权限:<uses-permi
- 1.定义Token的注解,需要Token校验的接口,方法上加上此注解import java.lang.annotation.ElementT
- 开篇Druid号称是Java语言中最好的数据库连接池,并且能够提供强大的监控和扩展功能。作为日常使用较多的数据库连接组件,纯粹个人兴趣研究下
- 一 概述DiffUtil是support-v7:24.2.0中的新工具类,它用来比较两个数据集,寻找出旧数据集-》新数据集的最小变化量。 说
- 有时候编译器、处理器的优化会导致runtime与我们设想的不一样,为此Java对编译器和处理器做了一些限制,JAVA内存模型(JMM)将这些
- 一般情况下SpringBoot以Jar包的形式进行打包打包 - jar包方式1、pom文件引入插件 <build> <pl
- 前言在我们日常的开发过程中通过打印详细的日志信息能够帮助我们很好地去发现开发过程中可能出现的Bug,特别是在开发Controller层的接口
- 话不多说,请看代码:/// <summary> /// 获取客户端IP /// </summary
- 一、前言canal:阿里巴巴 MySQL binlog 增量订阅&消费组件https://github.com/alibaba/ca
- 前言:我们都知道Android开发者的必备工具:AndroidStudio。是开发人员用来编译、测试的专用工具。今天在使用And
- 这篇文章主要介绍了设计模式在Spring框架中的应用汇总,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的
- 1.下载文件,将文件保存到本地。(只试用excel);2.对文件的标题进行检验;3.获取导入的批次(取一个表的一个值,加1);4.循环获取文
- 一种方法是可以在窗体的属性面板将窗体的 ControlBox属性设置为false,或者在窗体的构造函数中这样写:public F
- 结构体概念在C#中,结构体是值类型,一般适用于表示类似Point、Rectangle、Color的对象值类型能够降低对堆的管理、使用。降低垃
- ArrayList类List集合的实例化:List<String> l = new ArrayList<String>
- 这篇文章主要介绍了spring cloud alibaba Nacos 注册中心搭建过程详解,文中通过示例代码介绍的非常详细,对大家的学习或
- 在拼接绝对路径的网址时,经常需要从Request.Url中获取根网址(比如https://git.oschina.net),然后与相对路径一