源码解读Mybatis List列表In查询实现的注意事项

转自:http://www.blogjava.net/xmatthew/archive/2011/08/31/355879.html

源码解读Mybatis List列表In查询实现的注意事项

在SQL开发过程中,动态构建In集合条件查询是比较常见的用法,在Mybatis中提供了foreach功能,该功能比较强大,它允许你指定一个集合,声明集合项和索引变量,它们可以用在元素体内。它也允许你指定开放和关闭的字符串,在迭代之间放置分隔符。这个元素是很智能的,它不会偶然地附加多余的分隔符。下面是一个演示示例:

  <select id="findByIdsMap" resultMap="BaseResultMap">
    Select
    <include refid="Base_Column_List" />
     from jria where ID in
     <foreach item="item" index="index" collection="list" 
                    open="(" separator="," close=")">
                   #{item}
            </foreach>
 </select> 

但由于官方文档对这块的使用,描述的比较简短,细节上也被忽略掉了(可能是开源项目文档一贯的问题吧),也使用不少同学在使用中遇到了问题。特别是foreach这个函数中,collection属性做什么用,有什么注意事项。由于文档不全,这块只能通过源代码剖析的方式来分析一下各个属性的相关要求。

collection属性的用途是接收输入的数组或是List接口实现。但对于其名称的要求,Mybatis在实现中还是有点不好理解的,所以需要特别注意这一点。

下面开始分析源代码(笔记使用的是Mybatis 3.0.5版本)

先找到Mybatis执行SQL配置解析的入口

MapperMethod.java类中 public Object execute(Object[] args) 该方法是执行的入口.

针对in集合查询,对应用就是 selectForList或SelctForMap方法。

但不管调用哪个方法,都会对原来JDK传入的参数 Object[]类型,通过 getParam方法转换成一个Object,那这个方法是做什么的呢?分析源码如下:

上图中标红的两处,很惊讶的发现,一个参数与多个参数的处理方式是不同的(后续很多同学遇到的问题,就有一大部分出自这个地方)。如果参数个数大于一个,则会被封装成Map, key值如果使用了Mybatis的 Param注解,则会使用该key值,否则默认统一使用数据序号,从1开始。这个问题先记下,继续分析代码,接下来如果是selectForList操作(其它操作就对应用相应方法),会调用DefaultSqlSession的public List selectList(String statement, Object parameter, RowBounds rowBounds) 方法

又一个发现,见源代码如下:

上图标红部分,对参数又做了一次封装,我们看一下代码

现在有点清楚了,如果参数类型是List,则必须在collecion中指定为list, 如果是数据组,则必须在collection属性中指定为 array.

现在就问题就比较清楚了,如果是一个参数的话,collection的值取决于你的参数类型。

如果是多个值的话,除非使用注解Param指定,否则都是数字开头,所以在collection中指定什么值都是无用的。下图是debug显示结果。

针对上面分析的结果,下面给出了一个使用的解决方案,希望对大家对帮助。

在使用这个功能是需要特别注意以下规则:
1. 当查询的参数只有一个时 
  findByIds(List<Long> ids)
 1.a 如果参数的类型是List, 则在使用时,collection属性要必须指定为 list
 <select id="findByIdsMap" resultMap="BaseResultMap">
         Select
         <include refid="Base_Column_List" />
         from jria where ID in                  <foreach item="item" index="index" collection="list"                          open="(" separator="," close=")">                        #{item}                </foreach>  </select> 
 
 findByIds(Long[] ids)
 1.b 如果参数的类型是Array,则在使用时,collection属性要必须指定为 array
  <select id="findByIdsMap" resultMap="BaseResultMap">                 select                 <include refid="Base_Column_List" />          from jria where ID in                  <foreach item="item" index="index" collection="array"                          open="(" separator="," close=")">                        #{item}                </foreach>  </select> 
 
2. 当查询的参数有多个时,例如 findByIds(String name, Long[] ids)
 这种情况需要特别注意,在传参数时,一定要改用Map方式, 这样在collection属性可以指定名称
         下面是一个示例
         Map<String, Object> params = new HashMap<String, Object>(2);
        params.put("name", name);
         params.put("ids", ids);
        mapper.findByIdsMap(params);
 
 <select id="findByIdsMap" resultMap="BaseResultMap">                 select                 <include refid="Base_Column_List" />          from jria where ID in                  <foreach item="item" index="index" collection="ids"                          open="(" separator="," close=")">                        #{item}                </foreach>   </select> 
 
 
完整的示例如下:
例如有一个查询功能,Mapper接口文件定义如下方法:
List<Jria> findByIds(Long... ids);
使用 in 查询的sql拼装方法如下:
 <select id="findbyIds" resultMap="BaseResultMap">                 select                 <include refid="Base_Column_List" />          from jria where ID in                  <foreach item="item" index="index" collection="array"                          open="(" separator="," close=")">                        #{item}                </foreach>  </select> 
时间: 2024-11-07 19:48:31

源码解读Mybatis List列表In查询实现的注意事项的相关文章

Presto源码解读1-客户端提交查询-吕信(原创)

客户端请求处理就是接收交互式/非交互式命令,通过httpclient发送到服务器(coordinator),客户端通过httpclient更新执行状态打印给用户看,最后收集结果打印给用户.启动Presto cli 客户端的代码文件为: presto-cli\src\main\java\com\facebook\presto\cli\Presto.java public final class Presto { private Presto() {} public static void main

MyBatis源码解读(3)——MapperMethod

在前面两篇的MyBatis源码解读中,我们一路跟踪到了MapperProxy,知道了尽管是使用了动态代理技术使得我们能直接使用接口方法.为巩固加深动态代理,我们不妨再来回忆一遍何为动态代理. 我相信在初学MyBatis的时候几乎每个人都会发出一个疑问,为什么明明是XXXDao接口,我没有用任何代码实现这个接口,但却能直接使用这个接口的方法.现在清楚了,动态代理.我们来写一个demo小程序来看看. 首先是一个Test.java的接口,只有一个say方法. 1 package day_16_prox

jdk1.8.0_45源码解读——HashMap的实现

jdk1.8.0_45源码解读——HashMap的实现 一.HashMap概述 HashMap是基于哈希表的Map接口实现的,此实现提供所有可选的映射操作.存储的是<key,value>对的映射,允许多个null值和一个null键.但此类不保证映射的顺序,特别是它不保证该顺序恒久不变.  除了HashMap是非同步以及允许使用null外,HashMap 类与 Hashtable大致相同. 此实现假定哈希函数将元素适当地分布在各桶之间,可为基本操作(get 和 put)提供稳定的性能.迭代col

solr源码解读(转)

solr源码解读(转)原文地址:http://blog.csdn.net/duck_genuine/article/details/6962624 配置 solr 对一个搜索请求的的流程 在solrconfig.xml会配置一个handler.配置了前置处理组件preParams,还有后置处理组件filterResult,当然还有默认的组件 [html] view plaincopy <requestHandler name="standard" class="solr

Retrofit2 源码解读

开源库地址:https://github.com/square/retrofit 解读版本:2.1.0 基本概念 Retrofit 是一个针对Java/Android类型安全的Http请求客户端. 基本使用如下: 首先定义一个接口,抽象方法的返回值必须为Call<XX>. public interface GitHubService { @GET("users/{user}/repos") Call<List<Repo>> listRepos(@Pa

EventBus3 源码解读

基本概念 EventBus是一款针对Android优化的发布/订阅事件总线库.简便了Activities, Fragments, 以及background threads之间的通信,使发送者与订阅者之间有效解耦. 基本使用 EventBus的使用也极其简单,只需三步即可. 定义一个事件类型. public class MessageEvent { public final String message; public MessageEvent(String message) { this.mes

jdk1.8.0_45源码解读——Set接口和AbstractSet抽象类的实现

jdk1.8.0_45源码解读——Set接口和AbstractSet抽象类的实现 一. Set架构 如上图: (01) Set 是继承于Collection的接口.它是一个不允许有重复元素的集合.(02) AbstractSet 是一个抽象类,它继承于AbstractCollection.AbstractCollection实现了Set中的绝大部分函数,为Set的实现类提供了便利.(03) HastSet 和 TreeSet 是Set的两个实现类.        HashSet依赖于HashMa

Jfinal启动源码解读

本文对Jfinal的启动源码做解释说明. PS:Jfinal启动容器可基于Tomcat/Jetty等web容器启动,本文基于Jetty的启动方式做启动源码的解读和分析,tomcat类似. 入口  JFinalConfig的继承类的Main方法为入口,实例代码继承类为:DemoConfig,Main方法如下: public static void main(String[] args) { /** * 特别注意:Eclipse 之下建议的启动方式 */ JFinal.start("WebRoot&

HttpClient 4.3连接池参数配置及源码解读

目前所在公司使用HttpClient 4.3.3版本发送Rest请求,调用接口.最近出现了调用查询接口服务慢的生产问题,在排查整个调用链可能存在的问题时(从客户端发起Http请求->ESB->服务端处理请求,查询数据并返回),发现原本的HttpClient连接池中的一些参数配置可能存在问题,如defaultMaxPerRoute.一些timeout时间的设置等,虽不能确定是由于此连接池导致接口查询慢,但确实存在可优化的地方,故花时间做一些研究.本文主要涉及HttpClient连接池.请求的参数