Mybatis分页中遇到的坑2
Mybatis一对多嵌套查询和分页
需求:根据分类ID查询分类下所属的商品集合,每个商品又有一个图片集合。
类似的需求有很多,比如经典的一个用户有N个角色,一个角色有N个权限,那么通过用户的id来查询角色和权限数据等等。
至于分页插件,无论是Mybatis-PageHelper还是Mybatis-Plus都可以辅助,这里主要记录不同查询方式对分页的影响。
先展示结果:
Copy{
    "code": 0,
    "msg": "success",
    "data": {
        "total": 9,
        "size": 2,
        "pages": 5,
        "current": 1,
        "records": [
            {
                "id": 1,
                "code": "1410854032",
                "name": "Esmeralda Kilback",
                "categoryId": "1",
                "originPrice": 359,
                "price": 103,
                "sales": 299,
                "commentCount": 0,
                "freight": 1,
                "detail": "这里是商品详情",
                "createdAt": "2018-04-09 18:52:05",
                "updatedAt": "2018-04-24 23:41:49",
                "images": [
                    {
                        "id": 40,
                        "productId": "1",
                        "link": "uploads/product/201804/18/78a6e4e4d73bfc64b7aef88a90e7f192.png",
                        "createdAt": "2018-04-09 18:52:05",
                        "updatedAt": "2018-04-18 16:37:09"
                    },
                    {
                        "id": 41,
                        "productId": "1",
                        "link": "uploads/product/201804/18/fffdccaa36a8475ed3d2c71c2f43cb86.png",
                        "createdAt": "2018-04-09 18:52:05",
                        "updatedAt": "2018-04-18 16:37:09"
                    },
                    {
                        "id": 301,
                        "productId": "1",
                        "link": "uploads/product/201804/18/68b18cbcb090a94123abd9d729528370.png",
                        "createdAt": "2018-04-18 16:35:56",
                        "updatedAt": "2018-04-18 16:35:56"
                    }
                ]
            },
            {
                "id": 8,
                "code": "1925117917",
                "name": "Edgardo Osinski",
                "categoryId": "1",
                "originPrice": 389,
                "price": 154,
                "sales": 199,
                "commentCount": 0,
                "freight": 14,
                "detail": "这里是商品详情...5052 Kyler Walk Suite 921",
                "createdAt": "2018-04-09 18:52:05",
                "updatedAt": "2018-04-09 18:52:05",
                "images": [
                    {
                        "id": 58,
                        "productId": "8",
                        "link": "uploads/default.png",
                        "createdAt": "2018-04-09 18:52:05",
                        "updatedAt": "2018-04-09 18:52:05"
                    },
                    {
                        "id": 59,
                        "productId": "8",
                        "link": "uploads/default2.png",
                        "createdAt": "2018-04-09 18:52:05",
                        "updatedAt": "2018-04-09 18:52:05"
                    },
                    {
                        "id": 60,
                        "productId": "8",
                        "link": "uploads/default3.png",
                        "createdAt": "2018-04-09 18:52:05",
                        "updatedAt": "2018-04-09 18:52:05"
                    }
                ]
            }
        ]
    }
}
定义模型
Product用于数据库映射,为了保持其简洁,其他的二次封装不在Product里进行,而用继承的方式。
定义模型ProductVo,
Copy@Data
public class ProductVo extends Product {
    private List<ProductImage> images;
}
方式1:结果查询
- 在ProductsMapper.xml中定义select语句,一次性将关联数据全部查询出来,然后进行结果映射
Copy    <select id="selectProductsBycategoryId"  resultMap="productsListMap">
        select
            p.id,
            p.name,
            p.code,
            ...
            i.id images_id,
            i.product_id images_product_id,
            ...
        from products p
        inner join product_images i on p.id = i.product_id
        where p.category_id = #{id}
    </select>
- 定义productsListMap结果映射
Copy    <resultMap id="productsListMap" type="com.longke.mallb2c.entity.vo.ProductVo" extends="BaseResultMap">-->
        <collection property="images" columnPrefix="images_" resultMap="com.longke.mallb2c.mapper.ProductImagesMapper.BaseResultMap"/>
    </resultMap>
注意:
- property就是在- ProductVo中定义的商品图片集合- images字段
- 用到了columnPrefix列前缀,只是别名前缀,跟数据库内的字段无关,这个images_别名前缀跟select中定义别名时要保持一致。
- 用到了extends继承已有的BaseResultMap,不用在这里再重新写Product表的每个字段的映射了。mapper.xml自动生成工具都会帮我们生成这个BaseResultMap,直接继承即可。
- collection用于一对多查询,查询的结果映射直接复用- ProductImagesMapper中定义的- BaseResultMap
总结
优点
- 一次性查询,集中映射,简单,效率
缺点
- 会将collection中查询到的条数作为分页的约束条件,导致分页数据不准确。
比如想查page=1,limit=10的数据,本来期望的是查询出10个商品,然后这10个商品分别再嵌套查询出自己的商品图片集合。但是会发现,可能商品只有两三个,每个下面都带了自己的商品图片集合。
原因:
先通过表连接把表记录关联进来了,如果有3个商品,关联图片表之后每个商品有4条图片记录,那么其实这时候虽然只有三个商品,但是这个内存中的临时表已经有12条记录了,在语句的最后加上 limit 0,10,其实分页的时候分的是这12条记录。最终就会导致最终的映射结果只出现了3个商品,而非我们期望的10个商品。
方式2:嵌套查询
- 在ProductsMapper.xml中定义select语句
Copy   <select id="selectProductsBycategoryId"  resultMap="productsListMap">
        select <include refid="Base_Column_List"/>
        from products
        where category_id = #{id}
    </select>
- 定义productsListMap结果映射
Copy    <resultMap id="productsListMap" type="com.longke.mallb2c.entity.vo.ProductVo" extends="BaseResultMap">
        <collection property="images"  ofType="com.longke.mallb2c.entity.ProductImage"
                    column="{productId=id}" select="com.longke.mallb2c.mapper.ProductImagesMapper.selectByProductId">
        </collection>
    </resultMap>
注意:
- column是参数传递,即将Product的哪个属性传递给嵌套的查询语句,- {productId=id}代表将Product的- id属性传递给参数- productId
- select直接使用- ProductImagesMapper中定义的select语句
- 在ProductImagesMapper定义selectByProductId查询语句
Copy    <select id="selectByProductId" resultMap="BaseResultMap">
        SELECT <include refid="Base_Column_List"/>
        from product_images
        where product_id = #{productId}
    </select>
总结
优点
- 准确分页
缺点
- 没有解决N+1的问题,多条SQL查询语句,效率太差
Mybatis分页中遇到的坑2的更多相关文章
- Mybatis分页中遇到的坑3
		Mybatis Mapper.xml 配置文件中 resultMap 节点的源码解析 相关文章 Mybatis 解析配置文件的源码解析 Mybatis 类型转换源码分析 Mybatis 数据源和数 ... 
- Mysql系列八:Mycat和Sharding-jdbc的区别、Mycat分片join、Mycat分页中的坑、Mycat注解、Catlet使用
		一.Mycat和Sharding-jdbc的区别 1)mycat是一个中间件的第三方应用,sharding-jdbc是一个jar包 2)使用mycat时不需要改代码,而使用sharding-jdbc时 ... 
- Mybatis分页和Spring的集成
		写了一个Mybatis分页控件,在这记录一下使用方式. 在Maven中加入依赖: ? 1 2 3 4 5 6 7 8 9 <dependencies> ... <depe ... 
- mybatis分页插件以及懒加载
		1. 延迟加载 延迟加载的意义在于,虽然是关联查询,但不是及时将关联的数据查询出来,而且在需要的时候进行查询. 开启延迟加载: <setting name="lazyLoading ... 
- Mybatis分页插件PageHelper正确的用法(网上有2篇不够科学的文章)
		今天下午在Mybatis项目中.实现分页.由于我是后加入项目中的,Leader用的是PageHelper这个组件.可是我在实际使用的过程中遇到了2个大问题. 1.p=2#comments" ... 
- Mybatis分页插件PageHelper正确的使用方法(网上有2篇不够科学的文章)
		今天下午在Mybatis项目中,实现分页.因为我是后加入项目中的,Leader用的是PageHelper这个组件,但是我在实际使用的过程中遇到了2个大问题. 1.http://www.oschina. ... 
- Mybatis分页插件PageHelper的配置和使用方法
		Mybatis分页插件PageHelper的配置和使用方法 前言 在web开发过程中涉及到表格时,例如dataTable,就会产生分页的需求,通常我们将分页方式分为两种:前端分页和后端分页. 前端分 ... 
- Mybatis分页插件PageHelper使用
		一. Mybatis分页插件PageHelper使用 1.不使用插件如何分页: 使用mybatis实现: 1)接口: List<Student> selectStudent(Map< ... 
- mybatis分页练手
		最近碰到个需求,要做个透明的mybatis分页功能,描述如下:目标:搜索列表的Controller action要和原先保持一样,并且返回的json需要有分页信息,如: @ResponseBody @ ... 
随机推荐
- react面试宝典
			调用 setState 之后发生了什么? 在代码中调用setState函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation).经过调和过程 ... 
- ElasticSearch(十四) _search api search timeout 机制
			语法:timeout=10ms,timeout=1s,timeout=1m GET /_search?timeout=10m timeout:默认无timeout,latency平衡completen ... 
- STM32 CAN通信
			最近在STM32上开发CAN通信相关内容,转载一篇个人认为不错的文章,看完了基本算明白了,能够实际操作了. 原文地址: https://blog.csdn.net/ludaoyi88/article ... 
- jQuery事件函数位置放置的两种方法
			jQuery 事件函数 jQuery 事件处理方法是 jQuery 中的核心函数. 事件处理程序指的是当 HTML 中发生某些事件时所调用的方法. 通常会把 jQuery 代码放到 <head& ... 
- s:if
			<s:iterator value="value[3]" id="ques" status="s"> <s:if test ... 
- 1022. Fib数列
			https://acm.sjtu.edu.cn/OnlineJudge/problem/1022 Description 定义Fib数列:1,1,2,3,5,8,13,…1,1,2,3,5,8,13, ... 
- linux系统配置之PATH环境变量的设置(centos)
			Centos系统下修改环境变量PATH路径的方法 电脑脑中必不可少的就是操作系统.而Linux的发展非常迅速,有赶超微软的趋势.这里介绍Linux的知识,让你学好应用Linux系统.比如要把/etc/ ... 
- hdu-5754 Life Winner Bo(博弈)
			题目链接: Life Winner Bo Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/ ... 
- hdu-5003 Osu!(水题)
			题目链接: Osu! time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Prob ... 
- windbg调试堆破坏
			堆破坏 所谓的堆破坏,是说没控制好自己的指针,把不属于你分配的那块内存给写覆盖了.这块内存可能是你程序的数据,也可能是堆的管理结构.那么这个会导致怎样的后果呢?可能的情况我们来yy下 把程序里的计算结 ... 
