`
234390216
  • 浏览: 10232937 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
博客专栏
A5ee55b9-a463-3d09-9c78-0c0cf33198cd
Oracle基础
浏览量:462622
Ad26f909-6440-35a9-b4e9-9aea825bd38e
springMVC介绍
浏览量:1775515
Ce363057-ae4d-3ee1-bb46-e7b51a722a4b
Mybatis简介
浏览量:1398355
Bdeb91ad-cf8a-3fe9-942a-3710073b4000
Spring整合JMS
浏览量:395022
5cbbde67-7cd5-313c-95c2-4185389601e7
Ehcache简介
浏览量:679982
Cc1c0708-ccc2-3d20-ba47-d40e04440682
Cas简介
浏览量:530892
51592fc3-854c-34f4-9eff-cb82d993ab3a
Spring Securi...
浏览量:1183946
23e1c30e-ef8c-3702-aa3c-e83277ffca91
Spring基础知识
浏览量:467912
4af1c81c-eb9d-365f-b759-07685a32156e
Spring Aop介绍
浏览量:151390
2f926891-9e7a-3ce2-a074-3acb2aaf2584
JAXB简介
浏览量:68153
社区版块
存档分类
最新评论

Mybatis介绍之参数传递

阅读更多

9       Mybatis介绍之参数传递

9.1     使用Mapper接口时参数传递方式

       Mybatis在使用Mapper接口进行编程时,其实底层是采用了动态代理机制,表面上是调用的Mapper接口,而实际上是通过动态代理调用的SqlSession的对应方法,如selectOne(),有兴趣的朋友可以查看DefaultSqlSessiongetMapper()方法实现,其最终会获得一个代理了Mapper接口的MapperProxy对象。MapperProxy对象在调用Mapper接口方法时会把传递的参数做一个转换,然后把转换后的参数作为入参调用SqlSession对应的操作方法(如selectOneinsert等)。转换过程可以参考MapperMethodexecute()方法实现。简单来说是以下规则:

1、如果传递过来是单参数,且没有以@Param注解进行命名,则直接将单参数作为真实的参数调用SqlSession的对应方法。

2、如果传递过来的不是单参数或者是包含以@Param注解进行命名的参数,则会将对应的参数转换为一个Map进行传递。具体规则如下:

2.1、      会把对应的参数按照顺序以param1param2paramN这样的形式作为Key存入目标Map中,第一个参数是param1,第N个参数是paramN

2.2、      如果参数是以@Param注解命名的参数,则以@Param指定的名称作为Key存入目标Map中。

2.3、      如果参数不是以@Param注解命名的,则按照顺序以01N这样的形式作为Key存入目标Map中,第一个参数是0,第N个参数是N

 

       我们在Mapper.xml中定义SQL语句中用到的变量最终是以Mybatis按照上述规则把入参转换后的形式来进行解析的。

 

9.2     参数引用#$的区别

       MybatisMapper.xml文件中为了构建动态的SQL,我们通常需要通过引用传递的参数来达到对应的效果。在引用传递的参数的时候有两种方式,一种是通过“#{var}”的形式引用,一种是通过“${var}”的形式引用。前者会被以预编译的方式传递,即会生成PreparedStatement来处理,对应的参数在SQL中会以“?”占位,然后调用PreparedStatement的相应API设值;而后者则会先处理好变量再执行相应的SQL,即对应的变量会先被替换,再执行替换了变量的SQL。在一个Mapper映射语句中我们可以同时使用“#”和“$”处理变量。

 

9.3     采用#{var}的形式处理变量

       采用#{var}的形式来引用变量时,其中的变量会在解析Mapper.xml文件中的语句时就被替换为占位符“?”,同时通过ParameterMapping类记录对应的变量信息。在真正执行对应的语句时会用传递的真实参数根据ParameterMapping信息给PreparedStatement设置参数,具体可参考PreparedStatementHandlerparameterize()方法实现。对应的变量在解析的时候都是以传递进来的参数为基础来解析的,根据前面的介绍我们知道,传递进来的参数只有两种情况,一种是原始类型的单参数,一种是被Mybatis封装过的Map。那对应的参数Mybatis的如何解析的呢?有三种情况。

       第一种,如果真实传递进来的是原始类型,那么对应的属性解析就是就是基于原始类型的属性。比如我们真是传递进来的一个User类型的对象,User对象有idname等属性,那么我们在Mapper语句中采用#{var}形式访问变量的时候就可以使用#{id}#{name}。这跟使用${var}形式访问变量是不同的,使用${var}形式时我们需要使用${_parameter.id}${_parameter.name}

   <insert id="insert" parameterType="com.elim.learn.mybatis.model.User" useGeneratedKeys="true" keyColumn="id" keyProperty="id">

      insert into t_user(name,username,email,mobile) values(#{name},#{username},#{email},#{mobile})

   </insert>

 

       第二种,如果真实传递进来的是一个Map,那么对应的变量则可以是这个Map里面的Key。比如下面示例中我们的接口方法对应的是两个参数,那最终到执行Mapper语句时会被封装成一个MapMap里面的Key包含name1param1param2,那么我们在使用的时候就可以使用这些变量。

   List<User> findByNameAndMobile(@Param("name") String name, String mobile);

 

   <select id="findByNameAndMobile" resultType="com.elim.learn.mybatis.model.User">

      select id,name,username,email,mobile from t_user where name=#{name} and mobile=#{1}

   </select>

 

       第三种,真实传递进来的参数是可以被已经注册了的TypeHandler进行处理的,则直接使用真实传递进来的参数作为真实的变量值。这也是为什么我们经常可以在Mapper接口里面写delete(Integer id),然后在Mapper语句中直接写#{id}的原因,因为这个时候传递进来的参数是Integer类型,是可以直接被处理的,Mybatis将直接拿它的值作为当前变量id的值,而不会去取传递进来的值的id属性。换句话说,这个时候我们在Mapper语句中定义的变量可以随便命名,随便怎么命名都可以被Mybatis正确的处理。但是通常我们会把它命名为与接口方法参数名称一致,方便阅读。

   void delete(Long id);

 

   <insert id="delete" parameterType="java.lang.Long">

      delete t_user where id=#{id}

   </insert>

 

       关于预编译的参数处理是由DefaultParameterHandler类的setParameters()方法处理,核心代码如下,欲了解更多,可以参考DefaultParameterHandler的完整代码。

  public void setParameters(PreparedStatement ps) {

    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());

    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();

    if (parameterMappings != null) {

      for (inti = 0; i < parameterMappings.size(); i++) {

        ParameterMapping parameterMapping = parameterMappings.get(i);

        if (parameterMapping.getMode() != ParameterMode.OUT) {

          Object value;

          String propertyName = parameterMapping.getProperty();

          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params

            value = boundSql.getAdditionalParameter(propertyName);

          } else if (parameterObject == null) {

            value = null;

          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {

            value = parameterObject;

          } else {

            MetaObject metaObject = configuration.newMetaObject(parameterObject);

            value = metaObject.getValue(propertyName);

          }

          TypeHandler typeHandler = parameterMapping.getTypeHandler();

          JdbcType jdbcType = parameterMapping.getJdbcType();

          if (value == null && jdbcType == null) {

            jdbcType = configuration.getJdbcTypeForNull();

          }

          try {

            typeHandler.setParameter(ps, i + 1, value, jdbcType);

          } catch (TypeException e) {

            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);

          } catch (SQLException e) {

            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);

          }

        }

      }

    }

  }

 

9.4     采用${var}的形式处理变量

       采用“${var}”的形式来引用变量时,其中的变量会在MappedStatement调用getBoundSql()方法获取对应的BoundSql时被替换。${var}中的var可以是如下取值:

1、内置的_parameter变量,对应转换后的传递参数,在只传递单参数且是没有使用@Param注解对参数命名的时候如果我们需要通过${var}的形式来访问传递的单参数,则可以使用${_parameter}

2、如果对应的Mapper接口方法是多参数或者拥有@Param命名的参数时可以使用param1paramN的形式;

3、如果对应的Mapper接口方法参数是@Param命名的方法参数,则可以使用@Param指定的名称;

4、如果对应的Mapper接口方法拥有多个参数,且拥有没有使用@Param命名的参数,那没有使用@Param命名的参数可以通过01N形式访问。

 

       根据上述规则如我们有一个findById的方法其接收一个Long类型的参数作为ID,当使用${var}的形式引用变量时则可以写成如下这样:

   <select id="findById" resultType="com.elim.learn.mybatis.model.User" parameterType="java.lang.Long" >

      select id,name,username,email,mobile from t_user where id=${_parameter}

   </select>

 

       当我们的Mapper接口方法参数使用了@Param命名的时候,我们还可以使用@Param指定的参数名。

public interface UserMapper {

   User findById(@Param("id") Long id);

}

 

   <select id="findById" resultType="com.elim.learn.mybatis.model.User" parameterType="java.lang.Long" >

      select id,name,username,email,mobile from t_user where id=${id}

   </select>

       注意,但是使用了@Param对单参数命名后我们就不能再在Mapper语句中通过${_parameter}来引用接口方法参数传递过来的单参数了,因为此时其已经被包装为一个Map了,如果要通过_parameter来引用的话,我们应该使用${_parameter.param1}${_parameter.varName},对于上面的示例来说就是${_parameter.param1}${_parameter.id}

 

       下面我们来看一个传递多参数的,假设我们有如下这样的一个Mapper语句及对应的Mapper接口方法,这个Mapper接口方法接收两个参数,第一个参数是用@Param注解指定了参数名为name,第二个参数是没有使用注解的,具体如下。

   <!-- 当对应的接口方法传递多个参数时,可以不指定parameterType参数,就算指定了也没用,因为这个时候默认是Map -->

   <select id="findByNameAndMobile" resultType="com.elim.learn.mybatis.model.User">

      select id,name,username,email,mobile from t_user where name='${name}' and mobile='${1}'

   </select>

 

   List<User> findByNameAndMobile(@Param("name") String name, String mobile);

 

     那我们的Mapper.xml文件中的对应语句需要Mapper接口方法参数时有哪几种方式呢?按照之前的规则,对于第一个方法参数name而言,因为使用了@Param指定了参数名name,所以我们可以在Mapper.xml文件中通过变量nameparam1来引用它,而第二个参数mobile是没有使用@Param指定参数名的,则我们可以使用param2和参数相对位置“1”来引用它。如上面的示例中,我们就是通过第二个参数的相对位置“1”来引用的。如果我们把第二个参数改为${mobile}是引用不到,且系统会报如下错误,有兴趣的朋友可以试一下。

org.apache.ibatis.binding.BindingException: Parameter 'mobile' not found. Available parameters are [1, name, param1, param2]

 

       一般情况下为了防止SQL注入问题,是不建议直接在where条件中使用${var}的形式访问变量的,通常会用预编译形式的#{var}。而${var}往往是用来传递一些非where条件的内容,比如排序信息,具体用法请根据实际情况决定。

 

9.5     参考文档

org.apache.ibatis.session.defaults.DefaultSqlSession源码

org.apache.ibatis.binding.MapperMethod源码

org.apache.ibatis.executor.statement.PreparedStatementHandler源码

org.apache.ibatis.scripting.defaults.DefaultParameterHandler源码

 

(注:本文是基于Mybatis3.3.1所写,写于20161222日星期四)

 

 

 

0
1
分享到:
评论
1 楼 ws715 2017-02-20  

相关推荐

    mybatis 动态sql及参数传递

    参数传递 单个参数传递 多个参数传递 传入单个实体(JavaBean/Map) 传入多个实体 传入集合 使用场景 在实际开发过程中,我们往往需要编写复杂的SQL语句,拼接稍有不注意就会导致错误,Mybatis给开发者提供了动态SQL,...

    总结--Mybatis传递参数的几种方法

    Mybatis 传递参数的几种方法 Mybatis 中传递参数有多种方法,以下是其中的四种: 1. 传递单个参数 在 Mybatis 中,传递单个参数非常简单。可以直接将参数写在方法中,参数类型可以是 String,也可以是基本类型,...

    实现mybatis-只有一个参数传递的查询+代码+注释

    本文将详细讲解如何在MyBatis中实现一个只有一个参数传递的查询,包括配置、映射文件和Java代码,以及相关注释。 首先,搭建MyBatis环境是基础步骤。你需要在项目中添加MyBatis的依赖,如`pom.xml`文件中引入Maven...

    Mybatis参数传递1

    总之,MyBatis的参数传递方式灵活多样,可以根据实际情况选择合适的方法。在编写SQL映射文件时,要注意参数类型的正确设置以及如何在XML中引用这些参数。同时,合理使用`if`等标签进行条件判断,可以提高SQL的可读性...

    MyBatis传入多个参数的问题

    #### 五、多参数传递之注解方式 使用`@Param`注解可以更清晰地标识多参数。例如: ```java public AddrInfo getAddrInfo(@Param("corpId") int corpId, @Param("addrId") int addrId); ``` 对应的XML映射文件中的...

    postgresql + mybatis传入时间参数的问题.md

    我们看到, 直接在可视化工具里用SQL写 ccf.last_update_timestamp between TIMESTAMP '2019-12-13' AND TIMESTAMP '2019-12-13' 甚至去掉 TIMESTAMP 都是可以的, pgSQL可以自动把字符串给你转换成日期格式, ...

    Mybatis框架 mapper.xml文件中parameterType传递参数常用的几种方式.pdf

    在Mybatis框架中,mapper.xml文件是定义SQL语句、映射规则以及参数传递方式的重要组成部分。正确地使用parameterType传递参数对于开发高质量的应用程序至关重要。以下是几种常用的parameterType参数传递方式以及#和$...

    深入了解MyBatis参数

    本文将深入了解MyBatis参数,了解参数处理过程,参数传递方式,参数类型等知识点。 参数处理过程 ---------------- 在MyBatis中,参数处理是通过MapperMethod.java中的convertArgsToSqlCommandParam方法来实现的。...

    Mybatis多参数查询与列表查询不同方式实现

    1. **Map参数传递**: 当我们需要传递多个参数时,可以使用Map对象。在Mapper XML文件中,可以通过`&lt;foreach&gt;`标签遍历Map的键值对。例如,查询用户根据用户名和年龄: ```xml SELECT * FROM user WHERE ...

    mybatis动态sql及参数的传递

    本篇将深入探讨MyBatis中的动态SQL和参数传递机制。 动态SQL是MyBatis的一大亮点,它使得SQL语句可以根据条件进行动态构建,极大地提高了代码的可读性和维护性。在MyBatis中,动态SQL主要通过`&lt;if&gt;`, `&lt;choose&gt;`, `...

    MyBatis3传递多个参数(Multiple Parameters)

    MyBatis3传递多个参数是MyBatis框架中的一种重要特性,它允许开发者在执行数据库查询时传递多个参数,有多种方式可以实现多个参数的传递,本文将详细介绍四种常见的方法。 方法一:使用arg0、arg1、arg2等 在...

    Mybatis传list参数调用oracle存储过程的解决方法

    但是,如何将List类型参数传递给Oracle存储过程,实现批量插入数据却是一个棘手的问题,本文将给大家介绍Mybatis传List参数调用Oracle存储过程的解决方法。 使用Mybatis传List参数调用Oracle存储过程的解决方法 在...

    MyBatis中Foreach参数问题.doc

    在使用MyBatis进行数据库操作时,经常会遇到一些参数传递的问题,特别是在使用`foreach`语句处理集合数据时,很容易出现“Parameter 'xxxList' not found. Available parameters are [Collection,list]”这样的错误...

    mybatis利用association或collection传递多参数子查询

    "MyBatis多参数子查询使用association或collection" MyBatis是一款流行的Java持久层框架,提供了多种方式来实现复杂的数据库查询操作。其中,使用association或collection来传递多参数子查询是一种常用的方法。本文...

    Mybatis多参数及实体对象传递实例讲解

    在Mybatis中,参数传递是日常开发中必不可少的一部分。它涉及到如何将Java代码中的参数映射到SQL查询语句中,以便正确执行数据库操作。本文将深入探讨Mybatis的多参数传递以及实体对象的使用方法。 1. **多参数传递...

    mybatis多条件查询处理方案(查询条件为数据库字段非传递参数).pdf

    在MyBatis中,`&lt;if&gt;`标签是最常用的动态SQL元素之一,它的`test`属性通常用于判断传递的参数是否满足某个条件。例如,在商品查询的场景中,我们可能需要根据商品类型来过滤结果,如下所示: ```xml SELECT * FROM...

    Mybatis参数传递示例代码

    "Mybatis参数传递示例代码" Mybatis参数传递是Mybatis框架中一个非常重要的概念,它允许开发人员将参数传递给SQL语句,以实现更加灵活和可扩展的数据库交互。以下是关于Mybatis参数传递的详细知识点: 一、SQL语句...

    MyBatis.pdf

    MyBatis 提供了多种参数传递方式,包括单个参数直接传递、多个参数封装成 Map 集合作为参数传递和封装成对象传递。 MyBatis 的事务控制 MyBatis 提供了两种事务控制方式:自动提交和手动提交。自动提交方式下,...

    走进MyBatis世界之基础入门

    MyBatis是一款优秀的ORM框架,本课程讲解MyBatis的基础知识,主要内容包括:MyBatis增、删、改、查,MyBatis与Spring集成,association和collection映射,声明式事务管理,MyBatis参数传递的几种方式。

Global site tag (gtag.js) - Google Analytics