MyBatis 入门系列【4】 参数映射

258 阅读1分钟

1. 参数映射

MybatisMapper.xml语句中parameterTypeSQL语句传参有两种方式:#{}${}

对于大多数简单的使用场景,你都不需要使用复杂的参数,比如:

    <!--根据ID查询用户-->
    <select id="selectOneById" resultType="user" >
    select
        *
     from base_user where user_id = #{id}
  </select>

上面的这个示例说明了一个非常简单的命名参数映射。鉴于参数类型(parameterType)会被自动设置为int,这个参数可以随意命名。

原始类型或简单数据类型(比如 IntegerString)因为没有其它属性,会用它们的值来作为参数。 然而,如果传入一个复杂的对象,行为就会有点不一样了。比如:

    <insert id="insertUser" parameterType="user" useGeneratedKeys="true" keyColumn="user_id" keyProperty="userId">
        INSERT INTO base_user (user_id,user_name,login_name)
        VALUES (#{user.userId},#{user.userName},#{user.loginName})
    </insert>

如果 User 类型的参数对象传递到了语句中,会查找 idusernamepassword 属性,然后将它们的值传入预处理语句的参数中。

1.1 单个基本类型参数

单个的基本类型参数,Mybatis不会做特殊处理,直接使用#{参数名}取出参数值。

示例:

    User selectOneById(Long id);
    <!--根据ID查询用户-->
    <select id="selectOneById" resultType="user" >
    select
        *
     from base_user where user_id = #{id}
  </select>

1.2 多个基本类型参数

多个基本类型参数参数时,Mybatis会做特殊处理。

例如以下映射文件:

    User selectOneById(Long id,String userName);
    <select id="selectOneById" resultType="user" >
    select
        *
     from base_user where user_id = #{id} and user_name=#{userName}
  </select>

多个参数会被封装成一个SortedMap\<Integer, String> ,比如上述代码中, 会分装为{ 0:arg0,1:arg1},那么在xml中使用#{id}#{userName},则无法从Map中获取到对应的值,这种情况时,Mybatis提供了多种方式获取参数。

方式1:使用[arg1, arg0, param1, param2]获取

arg0或者param1表示第一个参数,可以通过设置#{arg0}或者#{param1}获取第一个参数,这种方法不建议使用,sql层表达不直观,且一旦顺序调整容易出错。

示例:

    <!--根据ID查询用户-->
    <select id="selectOneById" resultType="user" >
    select
        *
     from base_user where user_id = #{param1} and user_name=#{param2}
  </select>

方式2:使用@Param注解设置参数名

使用@Param注解指定参数名称解析,使用#{@Param中的value值}即可获取参数

示例:

    User selectOneById(@Param("id") Long id,@Param("userName")String userName);
    <select id="selectOneById" resultType="user" >
    select
        *
     from base_user where user_id = #{id} and user_name=#{userName}
  </select>

1.3 POJO 类

参数较多时,超过三个一般建议使用对象封装,比如直接传入entity类,或者封装为Query对象。通#{属性名}取出传入的pojo的属性值。

示例:

    int insertUser( User user);
    <insert id="insertUser">
        INSERT INTO base_user (user_id,user_name,login_name)
        VALUES (#{userId},#{userName},#{loginName})
    </insert>

1.4 Map

也可以传入Map,使用#{key},传入对应的参数,但是不建议这么做,因为一个Map,别人一下子也看不出来塞的些啥。。。

示例:

    User selectOneById(Map<String,Object> map);
    <select id="selectOneById" resultType="user">
    select
        *
     from base_user where user_id = #{id} and user_name=#{userName}
  </select>

1.5 集合

如果是Collection (List、set)类型或者是数组,也会特殊处理,也是把传入的list或者数组封装在map中。

可以使用collection\[0]映射参数,如果是List还可以使用这个list\[0],数组使用array\[0]

示例:

   User selectOneById(List<Long> ids);
    <select id="selectOneById" resultType="user">
    select
        *
     from base_user where user_id = #{list[0]}
  </select>

2. 参数约束

#{}可以设置参数的一些规则,一般只须简单指定属性名,顶多要为可能为空的列指定 jdbcType,其他的事情交给 MyBatis 自己去推断就行了。

#{}支持配置很多属性,例如:

#{property,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler,numericScale=2,resultMap=departmentResultMap}

元素说明:

属性描述
property参数
javaTypeJAVA数据类型,通常可以根据参数对象的类型确定 javaType,除非该对象是一个 HashMap。这个时候,你需要显式指定 javaType 来确保正确的类型处理器(TypeHandler)被使用。
jdbcType 数据库数据类型,JDBC 要求,如果一个列允许使用 null 值,并且会使用值为 null 的参数,就必须要指定 JDBC 类型(jdbcType)。Mybatisnull都会处理为JDBC中的OtherMysql会识别,但是Oracle不能识别,此时就需要处理,需要地指定jdbcTypenull
typeHandler数据类型处理器
numericScale 对于数值类型,还可以设置 numericScale 指定小数点后保留的位数。
mode 使用存储过程时,存储过程有三种类型的参数,分别为 IN(输入参数),OUT(输出参数),INOUT(输入输出参数)。如果参数的 mode OUTINOUT,将会修改参数对象的属性值,以便作为输出参数返回。
resultMap如果 modeOUT(或 INOUT),而且 jdbcTypeCURSOR(也就是 OracleREFCURSOR),你必须指定一个 resultMap 引用来将结果集 ResultMap 映射到参数的类型上。要注意这里的 javaType 属性是可选的,如果留空并且 jdbcTypeCURSOR,它会被自动地被设为 ResultMap

3. #{}和${}的区别

默认情况下,使用 #{} 参数语法时,MyBatis 会创建 PreparedStatement 参数占位符,并通过占位符安全地设置参数(就像使用 ? 一样)。以预编译的形式,将参数设置到sql语句中,可以防止sql注入。

示例:

image.png

${}取出的值直接拼装在sql语句中,会有安全问题。

示例:

image.png

大多情况下,我们去参数的值都应该去使用#{}#{}方式能够很大程度防止sql注入。${}方式一般用于传入数据库对象,例如传入表名,一般能用#{}的就别用 ${}