1. 参数映射
Mybatis 的Mapper.xml语句中parameterType向SQL语句传参有两种方式:#{}和${}。
对于大多数简单的使用场景,你都不需要使用复杂的参数,比如:
<!--根据ID查询用户-->
<select id="selectOneById" resultType="user" >
select
*
from base_user where user_id = #{id}
</select>
上面的这个示例说明了一个非常简单的命名参数映射。鉴于参数类型(parameterType)会被自动设置为int,这个参数可以随意命名。
原始类型或简单数据类型(比如 Integer 和 String)因为没有其它属性,会用它们的值来作为参数。 然而,如果传入一个复杂的对象,行为就会有点不一样了。比如:
<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 类型的参数对象传递到了语句中,会查找 id、username 和 password 属性,然后将它们的值传入预处理语句的参数中。
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 | 参数 |
javaType | JAVA数据类型,通常可以根据参数对象的类型确定 javaType,除非该对象是一个 HashMap。这个时候,你需要显式指定 javaType 来确保正确的类型处理器(TypeHandler)被使用。 |
jdbcType | 数据库数据类型,JDBC 要求,如果一个列允许使用 null 值,并且会使用值为 null 的参数,就必须要指定 JDBC 类型(jdbcType)。Mybatis对null都会处理为JDBC中的Other,Mysql会识别,但是Oracle不能识别,此时就需要处理,需要地指定jdbcType 为null。 |
typeHandler | 数据类型处理器 |
numericScale | 对于数值类型,还可以设置 numericScale 指定小数点后保留的位数。 |
mode | 使用存储过程时,存储过程有三种类型的参数,分别为 IN(输入参数),OUT(输出参数),INOUT(输入输出参数)。如果参数的 mode 为 OUT 或 INOUT,将会修改参数对象的属性值,以便作为输出参数返回。 |
resultMap | 如果 mode 为 OUT(或 INOUT),而且 jdbcType 为 CURSOR(也就是 Oracle 的 REFCURSOR),你必须指定一个 resultMap 引用来将结果集 ResultMap 映射到参数的类型上。要注意这里的 javaType 属性是可选的,如果留空并且 jdbcType 是 CURSOR,它会被自动地被设为 ResultMap。 |
3. #{}和${}的区别
默认情况下,使用 #{} 参数语法时,MyBatis 会创建 PreparedStatement 参数占位符,并通过占位符安全地设置参数(就像使用 ? 一样)。以预编译的形式,将参数设置到sql语句中,可以防止sql注入。
示例:
${}取出的值直接拼装在sql语句中,会有安全问题。
示例:
大多情况下,我们去参数的值都应该去使用#{},#{}方式能够很大程度防止sql注入。${}方式一般用于传入数据库对象,例如传入表名,一般能用#{}的就别用 ${}。