mybatis框架下select带in批量查询,返回结果集无序问题

引言

在实际项目中有的时候会多次连接数据库查询,有的时候会带上in的语句,在二次查询的时候返回的数据集就是没有顺序的。比如我们在微博平台上搜索某个用户,为了搜索速度够快,在els那边一般只存user的id和username。当根据username匹配到用户,获取到id后,还会在本地的数据库中根据id去查询用户更多的信息,比如sex,signature等字段。在第一次查询的时候是按照username匹配程度来排序的,而第二次查询的结果希望能与第一次查询的结果顺序保持一致。

因此,如何保证in查询的结果与输入的一致是非常重要的。

实操

一. 辟谣

有人说只要把resultType中的HashMap转换成LinkedHaspMap,就可以保持一致(网址:https://blog.csdn.net/qq_29115715/article/details/69958264
经过博主亲自尝试,此种方法根本就无效!!

二. 利用sql原声语句

先来看一下普通的in语句出现的效果


图1.普通的in语句

利用order by field的效果


图2.利用order by field的效果

因此,利用mybatis的mapper.xml编写原生的xml也可以达到如此效果,代码如下:
接口层
@Repository
@Mapper
public interface UserMapper extends BaseDao<User> {
    List<User> yktestOrder(@Param("ids")List<Long> ids);
}

Mapper.xml

 <select id="yktestOrder" resultType="com.yuxun.fantuan.security.entity.User">
        select
        u.id,
        u.username
        FROM
        user u
        WHERE
        1 = 1
        <if test="null != ids and ids.size > 0">
        AND u.id IN
        <foreach item="item" index="index" collection="ids" open="(" separator="," close=")">
            #{item}
        </foreach>
            order by field(u.id,
            <foreach item="item" index="index" collection="ids" separator=",">
                #{item}
            </foreach>
            )
        </if>
    </select>

测试controller

    @Autowired
    private UserMapper userMapper;
    @PostMapping(value = "/yktest")
    @ResponseBody
    public RestResponse<String> yktest() throws Exception {

        List<Long> ids = new ArrayList<Long>(){{
            this.add(635L);
            this.add(1L);
            this.add(1179L);
        }};
        List<User> result1 = userMapper.selectBatchIds(ids);
        List<User> resutl2 = userMapper.yktestOrder(ids);
        System.out.println("result1");
        result1.forEach(System.out::println);
        System.out.println("result2");
        resutl2.forEach(System.out::println);
        return RestResponse.ok("执行完成");
    }

最终的console输出的结果如下:

图3.测试结果图

注意:特别要小心xml中order by field的括号写法

三. 自己编写utils方法进行转换

思路
  • 传入的对象是一个list<User>或者是一个list<Map<String,Object>>类型。我们知道User继承Object,但是List<User>并不继承List<Object>。所以我们只能拿Object去接收待转顺序的结果集Object result,并且我们要知道该结果集的类型Class<?> resultClass
  • 同理,我们要知道待排序的list和它的类型分别为Object order,Class<?> orderClass。以及我们还要知道根据结果集result中哪个字段进行排序,String orderName
  • 接着我们转换顺序的思想就是,拿一个中间的tempMap,类型为Map<orderClass, resultClass>。先遍历result,拿到每个元素的orderName的值当做tempMap的key,每个元素自身当做value。然后我们在遍历order,把其中的每个元素从temp的map之中get出来,放到最终的结果List<resultClass> returnList之中。
  • 最后把 returnList返回。因为我们知道查询结果都是List<>类型的,所以才可以确定返回的就是要List<resultClass>。
    PS:有几个点需要注意一下:
    • map取key值的方法就是get("id"),而如果是User对象取值方式是getId()。所以二者需要做类型区别判断。
    • order之中可能存在一些id,在result之中并不存在,所以最后的从temp的map之中取元素的时候要做取到结果是否为空的判断。

一句话概括就是用map作为中间变量,先把所有元素存进去,然后按照预想的顺序get出来add到返回的list中。

最终代码如下:

/**
     * 传入相应的查询结果和欲想顺序进行顺序重排
     *
     * @param result 传入的查询结果
     * @param resultClass 传入查询结果的类
     * @param order 传入的顺序List
     * @param orderClass 传入的顺序List的类型
     * @param orderName 传入的要排序的字段名
     * @param <resultClass>
     * @param <orderClass>
     * @return
     * @throws Exception
     */
    public static <resultClass, orderClass> List<resultClass> changeResultOrder(Object result, Class<?> resultClass,
                                                                                Object order, Class<?> orderClass, String orderName) {
        List<resultClass> returnList = new ArrayList<>();
        Map<orderClass, resultClass> tempMap = new HashMap<>();

        for (resultClass aoo : (List<resultClass>) result) {
            if (resultClass.isAssignableFrom(Map.class)) {
                Object b = MapUtils.getObject((Map) aoo, orderName);
                orderClass cb = (orderClass) b;
                if (orderClass.isAssignableFrom(Long.class)) {
                    cb = (orderClass) Long.valueOf(b + "");
                }
                tempMap.put(cb, aoo);
            } else {
                try {
                    Method method = aoo.getClass().getMethod("get" + toUpperFirstChar(orderName));
                    orderClass b = (orderClass) method.invoke(aoo);
                    tempMap.put(b, aoo);
                }catch (NoSuchMethodException | IllegalAccessException |InvocationTargetException e){
                    throw new FantuanRuntimeException("调用sqlHelpUtils出错,反射获取方法有误",e);
                }
            }
        }

        for (orderClass boo : (List<orderClass>) order) {
            resultClass getResult = tempMap.get(boo);
            if (null != getResult){
                returnList.add(getResult);
            }
        }
        return returnList;
    }

总结

个人比较推荐使用方法三,一是比较灵活,二是我们在用mybatis的时候很多时候会用mybatis的插件,不常在底层操作mapper.xml文件。正如博主在代码中用的

<dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>mybatis-plus</artifactId>
      <version>2.1.9</version>
</dependency>

在查询的时候就直接用

 List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);

此时用自己编写的代码去调整顺序就非常合适。

方法三的代码和相应的测试用例已经放在github上面,有需要的读者可以自行下载:
https://github.com/YukunWen/SqlHelperUtilsDemo

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,194评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,058评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,780评论 0 346
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,388评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,430评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,764评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,907评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,679评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,122评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,459评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,605评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,270评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,867评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,734评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,961评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,297评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,472评论 2 348

推荐阅读更多精彩内容