之前见到的所有语句都使用了简单的参数形式。但实际上,参数是 MyBatis 非常强大的元素。对于大多数简单的使用场景,你都不需要使用复杂的参数,比如:
<select id="selectUsers" resultType="User">
select id, username, password
from users
where id = #{id}
</select>
上面的这个示例说明了一个非常简单的命名参数映射。鉴于参数类型(parameterType
)会被自动设置为 int,这个参数可以随意命名。原始类型或简单数据类型(比如 Integer 和 String)因为没有其它属性,会用它们的值来作为参数。 然而,如果传入一个复杂的对象,行为就会有点不一样了。比如:
<insert id="insertUser" parameterType="User">
insert into users (id, username, password)
values (#{id}, #{username}, #{password})
</insert>
如果 User 类型的参数对象传递到了语句中,会查找 id
、username
和 password
属性,然后将它们的值传入预处理语句的参数中。
对传递语句参数来说,这种方式真是干脆利落。不过参数映射的功能远不止于此。
首先,和 MyBatis 的其它部分一样,参数也可以指定一个特殊的数据类型。
#{property,javaType=int,jdbcType=NUMERIC}
和 MyBatis 的其它部分一样,几乎总是可以根据参数对象的类型确定 javaType
,除非该对象是一个 HashMap
。这个时候,你需要显式指定 javaType
来确保正确的类型处理器(TypeHandler
)被使用。
JDBC
要求,如果一个列允许使用 null
值,并且会使用值为 null
的参数,就必须要指定 JDBC
类型(jdbcType
)。
要更进一步地自定义类型处理方式,可以指定一个特殊的类型处理器类(或别名),比如:
#{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}
参数的配置好像越来越繁琐了,但实际上,很少需要如此繁琐的配置。
对于数值类型,还可以设置 numericScale
指定小数点后保留的位数。
#{height,javaType=double,jdbcType=NUMERIC,numericScale=2}
最后,mode
属性允许你指定 IN
,OUT
或 INOUT
参数。如果参数的 mode
为 OUT
或 INOUT
,将会修改参数对象的属性值,以便作为输出参数返回。 如果 mode
为 OUT
(或 INOUT
),而且 jdbcType
为 CURSOR
(也就是 Oracle
的 REFCURSOR
),你必须指定一个 resultMap
引用来将结果集 ResultMap
映射到参数的类型上。要注意这里的 javaType
属性是可选的,如果留空并且 jdbcType
是 CURSOR
,它会被自动地被设为 ResultMap
。
#{department, mode=OUT, jdbcType=CURSOR, javaType=ResultSet, resultMap=departmentResultMap}
MyBatis 也支持很多高级的数据类型,比如结构体(structs),但是当使用 out 参数时,你必须显式设置类型的名称。比如(再次提示,在实际中要像这样不能换行):
#{middleInitial, mode=OUT, jdbcType=STRUCT, jdbcTypeName=MY_TYPE, resultMap=departmentResultMap}
尽管上面这些选项很强大,但大多时候,你只须简单指定属性名,顶多要为可能为空的列指定 jdbcType
,其他的事情交给 MyBatis 自己去推断就行了。
#{firstName}
#{middleInitial,jdbcType=VARCHAR}
#{lastName}
默认情况下,使用 #{}
参数语法时,MyBatis 会创建 PreparedStatement
参数占位符,并通过占位符安全地设置参数(就像使用 ? 一样)。 这样做更安全,更迅速,通常也是首选做法,不过有时你就是想直接在 SQL 语句中直接插入一个不转义的字符串。 比如 ORDER BY 子句,这时候你可以:
ORDER BY ${columnName}
这样,MyBatis 就不会修改或转义该字符串了。
当 SQL 语句中的元数据(如表名或列名)是动态生成的时候,字符串替换将会非常有用。 举个例子,如果你想 select 一个表任意一列的数据时,不需要这样写:
@Select("select * from user where id = #{id}")
User findById(@Param("id") long id);
@Select("select * from user where name = #{name}")
User findByName(@Param("name") String name);
@Select("select * from user where email = #{email}")
User findByEmail(@Param("email") String email);
// 其它的 "findByXxx" 方法
而是可以只写这样一个方法:
@Select("select * from user where ${column} = #{value}")
User findByColumn(@Param("column") String column, @Param("value") String value);
其中 ${column}
会被直接替换,而 #{value}
会使用 ?
预处理。 这样,就能完成同样的任务:
User userOfId1 = userMapper.findByColumn("id", 1L);
User userOfNameKid = userMapper.findByColumn("name", "kid");
User userOfEmail = userMapper.findByColumn("email", "noone@nowhere.com");
这种方式也同样适用于替换表名的情况。
用这种方式接受用户的输入,并用作语句参数是不安全的,会导致潜在的 SQL 注入攻击。因此,要么不允许用户输入这些字段,要么自行转义并检验这些参数。
多数据库支持如果配置了databaseIdProvider,你就可以在动态代码中使用名为“_databaseId”的变量来为不同的数据库构建...
问题Java程序员面对的最痛苦的事情之一就是在Java代码中嵌入SQL语句。这通常是因为需要动态生成SQL语句,不然我们可以将它们放到...
架构Hibernate 架构是分层的,作为数据访问层,你不必知道底层 API 。Hibernate 利用数据库以及配置数据来为应用程序提供持续性...
简介Hibernate 是由 Gavin King 于 2001 年创建的开放源代码的对象关系框架。它强大且高效的构建具有关系对象持久性和查询服务的...
Spring 编程式事务管理编程式事务管理方法允许你在对你的源代码编程的帮助下管理事务。这给了你极大地灵活性,但是它很难维护。...
Spring 是最受欢迎的企业级 Java 应用程序开发框架,数以百万的来自世界各地的开发人员使用 Spring 框架来创建性能好、易于测试...
Bean 后置处理器允许在调用初始化方法前后对 Bean 进行额外的处理。BeanPostProcessor 接口定义回调方法,你可以实现该方法...
当容器调用带有一组参数的类构造函数时,基于构造函数的 DI 就完成了,其中每个参数代表一个对其他类的依赖。接下来,我们将通过...