高产似母猪之中的母猪……
MyBatis 因为其支持定制化 SQL,存储过程等等,而且避免了几乎所有的JDBC 代码和手动设置参数,以及获取结果集等等这些操作,深受广大被折磨的程序员的欢迎.
由于环境配置已经存在,那么我就直接略过环境配置的环节,直接上干货啦!
1. XML 映射文件
官方说明是:由于MyBatis 的映射语句的强大,其映射器的XML 文件变得相对简单,相对于同功能的JDBC 代码,可以发现其省略了将近 95% 的代码. MyBatis 为了聚焦于 SQL 而建,用来尽可能的减少麻烦.
SQL 映射文件只有几个很少的顶级元素.下面是按照其应该被定义的顺序列出:
- Cache: 对给定 命名空间 的缓存配置
- Cache-ref : 对其他命名空间的 缓存配置 的引用
- resultMap: “最复杂且最强大”, 用来描述如何从 数据库结果集 之中加载对象.
- sql: 可被其他语句引用的 可重用语句块
- insert: 映射插入语句
- update: 映射更新语句
- delete: 映射删除语句
- select: 映射查询语句
1.1 select
查询语句在任何情况之下都是使用最频繁的功能。同样,MyBatis 的基本原则之一,就是将焦点和努力放在查询和结果映射的原因,简单查询的 select 元素是非常简单的,比如:
<select id="selectPerson" parameterType="int" resultType="hashmap">
SELECT * FROM PERSON WHERE ID = #{id}
</select>
这个语句的意义是:
- 名字被称为 selectPerson
- 接受一个 int 或者 Integer 类型的参数
- 返回一个 HashMap 类型的对象
其中的键是列名,值就是结果行之中的对应值。
注意其参数符号为
#{id}
这相当于告诉 MyBatis 创建一个有预处理语句(PreparedStatement) 的参数,在 JDBC 之中,这样的一个参数在 SQL 之中就会由一个“?”来表示,并且被传递到一个新的预处理语句之中,类似于这样:
//下面是类似功能的 JDBC 代码,而不是 MyBatis 代码。
String selectPerson = "SELECT * FROM PERSON WHERE ID=?";
PreparedStatement ps = conn.prepareStatement(selectPerson);
ps.setInt(1,id);
MyBatis 还有更多的地方节省时间和细节,这些会在后面的小节之中呈现。
Select 允许配置很多的属性来配置每条语句的作用细节。
<select
id="selectPerson"
parameterType="int"
parameterMap="deprecated"
resultType="hashmap"
resultMap="personResultMap"
flushCache="false"
useCache="true"
timeout="10"
fetchSize="256"
statementType="PREPARED"
resultSetType="FORWARD_ONLY">
下面是各种元素及其描述:
属性 | 描述 |
---|---|
id |
在命名空间中唯一的标识符,可以被用来引用这条语句。 |
parameterType |
将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler) 推断出具体传入语句的参数,默认值为未设置(unset)。 |
parameterMap | 这是引用外部 parameterMap 的已经被废弃的方法。请使用内联参数映射和 parameterType 属性。 |
resultType |
从这条语句中返回的期望类型的类的完全限定名或别名。 注意如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。可以使用 resultType 或 resultMap,但不能同时使用。 |
resultMap |
外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂映射的情形都能迎刃而解。可以使用 resultMap 或 resultType,但不能同时使用。 |
flushCache |
将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。 |
useCache |
将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true。 |
timeout |
这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖驱动)。 |
fetchSize |
这是一个给驱动的提示,尝试让驱动程序每次批量返回的结果行数和这个设置值相等。 默认值为未设置(unset)(依赖驱动)。 |
statementType |
STATEMENT,PREPARED 或 CALLABLE 中的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
resultSetType |
FORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等价于 unset) 中的一个,默认值为 unset (依赖驱动)。 |
databaseId |
如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。 |
resultOrdered |
这个设置仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况。 这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:false 。 |
resultSets |
这个设置仅对多结果集的情况适用。它将列出语句执行后返回的结果集并给每个结果集一个名称,名称是逗号分隔的。 |
1.2 insert,update 和 delete
其实格式和以前都几乎相同。 那么下面是一个例子:
<insert
id="insertAuthor"
parameterType="domain.blog.Author"
flushCache="true"
statementType="PREPARED"
keyProperty=""
keyColumn=""
useGeneratedKeys=""
timeout="20">
<update
id="updateAuthor"
parameterType="domain.blog.Author"
flushCache="true"
statementType="PREPARED"
timeout="20">
<delete
id="deleteAuthor"
parameterType="domain.blog.Author"
flushCache="true"
statementType="PREPARED"
timeout="20">
属性 | 描述 |
---|---|
id |
命名空间中的唯一标识符,可被用来代表这条语句。 |
parameterType |
将要传入语句的参数的完全限定类名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器推断出具体传入语句的参数,默认值为未设置(unset)。 |
parameterMap |
这是引用外部 parameterMap 的已经被废弃的方法。请使用内联参数映射和 parameterType 属性。 |
flushCache |
将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:true(对于 insert、update 和 delete 语句)。 |
timeout |
这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖驱动)。 |
statementType |
STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
useGeneratedKeys |
(仅对 insert 和 update 有用)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系数据库管理系统的自动递增字段),默认值:false。 |
keyProperty |
(仅对 insert 和 update 有用)唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey 子元素设置它的键值,默认值:未设置(unset )。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。 |
keyColumn |
(仅对 insert 和 update 有用)通过生成的键值设置表中的列名,这个设置仅在某些数据库(像 PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置。如果希望使用多个生成的列,也可以设置为逗号分隔的属性名称列表。 |
databaseId |
如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。 |
下面是示例:
<insert id="insertAuthor">
insert into Author (id,username,password,email,bio)
values (#{id},#{username},#{password},#{email},#{bio})
</insert>
<update id="updateAuthor">
update Author set
username = #{username},
password = #{password},
email = #{email},
bio = #{bio}
where id = #{id}
</update>
<delete id="deleteAuthor">
delete from Author where id = #{id}
</delete>
可见,其插入语句的配置规则更丰富,在插入语句之中,也有一些额外属性和子元素用来处理主键的生成,而且有多种生成方式。
有自动生成主键方式的数据库,可以设置useGeneratedKeys="true"
, 再把 keyProperty 设置到目标属性上面就可以了。例如,上面的 Author 表已经对 id 进行了自动生成的列类型,那么可以修改为:
<insert id="insertAuthor" useGeneratedKeys="true"
keyProperty="id">
insert into Author (username,password,email,bio)
values (#{username},#{password},#{email},#{bio})
</insert>
如果支持多行插入,那么也可以传入一个集合,并且返回自动生成的主键。
<insert id="insertAuthor" useGeneratedKeys="true"
keyProperty="id">
insert into Author (username, password, email, bio) values
<foreach item="item" collection="list" separator=",">
(#{item.username}, #{item.password}, #{item.email}, #{item.bio})
</foreach>
</insert>
如果不支持自动生成类型的数据库,也就是不支持自动生成主键的 JDBC 驱动,有另外一种方式来生成主键。下面是一个简单实例,可以看到在其中,<selectKey>
的部分代码作为一种简单的生成 Key 的方法。
<insert id="insertAuthor">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1
</selectKey>
insert into Author
(id, username, password, email,bio, favourite_section)
values
(#{id}, #{username}, #{password}, #{email}, #{bio}, #{favouriteSection,jdbcType=VARCHAR})
</insert>
上面这段代码对于不支持自动生成类型的数据库而言,其中的 selectKey
之中的语句会首先运行, Author 的 ID 会被设置,然后插入语句才会被调用。 换句话说,其实我们自己书写了一个和数据库之中自动生成主键类似的行为,但是是在xml 之中,所以我们保持了 Java 代码的简洁。
selectKey 的代码叙述如下:
<insert id="insertAuthor">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1
</selectKey>
insert into Author
(id, username, password, email,bio, favourite_section)
values
(#{id}, #{username}, #{password}, #{email}, #{bio}, #{favouriteSection,jdbcType=VARCHAR})
</insert>
属性 | 描述 |
---|---|
keyProperty |
selectKey 语句结果应该被设置的目标属性。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。 |
keyColumn |
匹配属性的返回结果集中的列名称。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。 |
resultType |
结果的类型。MyBatis 通常可以推断出来,但是为了更加精确,写上也不会有什么问题。MyBatis 允许将任何简单类型用作主键的类型,包括字符串。如果希望作用于多个生成的列,则可以使用一个包含期望属性的 Object 或一个 Map。 |
order |
这可以被设置为 BEFORE 或 AFTER。如果设置为 BEFORE,那么它会首先生成主键,设置 keyProperty 然后执行插入语句。如果设置为 AFTER,那么先执行插入语句,然后是 selectKey 中的语句 - 这和 Oracle 数据库的行为相似,在插入语句内部可能有嵌入索引调用。 |
statementType |
与前面相同,MyBatis 支持 STATEMENT,PREPARED 和 CALLABLE 语句的映射类型,分别代表 PreparedStatement 和 CallableStatement 类型。 |
1.3 sql
其可以被用来定义可重用的SQL 代码段。这些代码不仅可以被包含在其他语句之中,还可以被静态的设置参数。在不同的包含语句之中,可以设置不同的值到参数占位符上面。比如:
<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>
这个 SQL 语句可以被包含在其他语句之中,比如:
<select id="selectUsers" resultType="map">
select
<include refid="userColumns"><property name="alias" value="t1"/></include>,
<include refid="userColumns"><property name="alias" value="t2"/></include>
from some_table t1
cross join some_table t2
</select>
属性值,也可以被用来 include 元素的 内部语句或者是 refid 属性之中,比如:
<sql id="sometable">
${prefix}Table
</sql>
<sql id="someinclude">
from
<include refid="${include_target}"/>
</sql>
<select id="select" resultType="map">
select
field1, field2, field3
<include refid="someinclude">
<property name="prefix" value="Some"/>
<property name="include_target" value="sometable"/>
</include>
</select>
1.4 参数
其实之前见到的所有语句之中,使用的都是简单参数
<select id="selectUsers" resultType="User">
select id, username, password
from users
where id = #{id}
</select>
这个就是使用了一个非常简单的命名参数映射,参数类型设置成 int, 其就可以被设置成任何内容。简单类型 和 原始数据类型 ,比如 Integer 和 String, 因为没有相关属性,其会完全用参数值来替代。但是如果是在传入一个复杂对象的情况之下,就会:
<insert id="insertUser" parameterType="User">
insert into users (id, username, password)
values (#{id}, #{username}, #{password})
</insert>
其如果是 User 类型的参数对象传递到了语句之中,这三个属性将会被查找,在查找之后会传入预处理语句的参数之中。
JBDC 要求,如果一个列允许 null 值,且会传递值是 null 的参数,那么必须要指定 JDBC Type。
数值类型还有一个小数保留位数的设置:
#{height,javaType=double,jdbcType=NUMERIC,numericScale=2}
1.5 字符串替换
默认情况下,使用 #{} 格式的语法会导致 MyBatis 创建 PrepareStatement 参数占位符, 并且安全的设置参数(就像使用 ? 一样),这样更安全迅速,也是首选做法。但是有的时候想在 SQL 之中插入一个不转义的字符串,比如 ORDER BY,就可以这样使用:
ORDER BY ${columnName}
这个对于有多种情况的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);
// and more "findByXxx" method
而是:
@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 注入攻击,因此要么直接不允许用户输入这些字段,要么自行进行 转义并检验
1.6 id & result
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
这些是结果映射最基本的内容。id 和 result 元素都将一个列的值映射到一个简单数据类型(String, int, double, Date 等)的属性或字段。
这两者之间的唯一不同是,id 元素表示的结果将是对象的标识属性,这会在比较对象实例时用到。 这样可以提高整体的性能,尤其是进行缓存和嵌套结果映射(也就是连接映射)的时候。
两个元素都有一些属性:
属性 | 描述 |
---|---|
property |
映射到列结果的字段或属性。如果用来匹配的 JavaBean 存在给定名字的属性,那么它将会被使用。否则 MyBatis 将会寻找给定名称的字段。 无论是哪一种情形,你都可以使用通常的点式分隔形式进行复杂属性导航。 比如,你可以这样映射一些简单的东西:“username”,或者映射到一些复杂的东西上:“address.street.number”。 |
column |
数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。 |
javaType |
一个 Java 类的完全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。 |
jdbcType |
JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可能存在空值的列指定这个类型。 |
typeHandler |
我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的类型处理器。 这个属性值是一个类型处理器实现类的完全限定名,或者是类型别名。 |
1.7 构造方法
主要用在DTO 层面。
有些情况下,对于很少改变的值或者属性,都适合使用不可变类。 构造方法允许在初始化的时候设置属性的值,但是不暴露 getter 或者 setter
看看下面这个构造方法:
public class User {
//...
public User(Integer id, String username, int age) {
//...
}
//...
}
为了将结果注入构造方法,MyBatis 需要通过某种方式定位相应的构造方法。 在下面的例子中,MyBatis 搜索一个声明了三个形参的的构造方法,参数类型以 java.lang.Integer
, java.lang.String
和 int
的顺序给出。
<constructor>
<idArg column="id" javaType="int"/>
<arg column="username" javaType="String"/>
<arg column="age" javaType="_int"/>
</constructor>
当你在处理一个带有多个形参的构造方法时,很容易搞乱 arg 元素的顺序。 从版本 3.4.3 开始,可以在指定参数名称的前提下,以任意顺序编写 arg 元素。 为了通过名称来引用构造方法参数,你可以添加 @Param
注解,或者使用 ‘-parameters’ 编译选项并启用 useActualParamName
选项(默认开启)来编译项目。下面是一个等价的例子,尽管函数签名中第二和第三个形参的顺序与 constructor 元素中参数声明的顺序不匹配。
<constructor>
<idArg column="id" javaType="int" name="id" />
<arg column="age" javaType="_int" name="age" />
<arg column="username" javaType="String" name="username" />
</constructor>
属性 | 描述 |
---|---|
column |
数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。 |
javaType |
一个 Java 类的完全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。 |
jdbcType |
JDBC 类型,所支持的 JDBC 类型参见这个表格之前的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可能存在空值的列指定这个类型。 |
typeHandler |
我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的类型处理器。 这个属性值是一个类型处理器实现类的完全限定名,或者是类型别名。 |
select |
用于加载复杂类型属性的映射语句的 ID,它会从 column 属性中指定的列检索数据,作为参数传递给此 select 语句。具体请参考关联元素。 |
resultMap |
结果映射的 ID,可以将嵌套的结果集映射到一个合适的对象树中。 它可以作为使用额外 select 语句的替代方案。它可以将多表连接操作的结果映射成一个单一的 ResultSet 。这样的 ResultSet 将会将包含重复或部分数据重复的结果集。为了将结果集正确地映射到嵌套的对象树中,MyBatis 允许你 “串联”结果映射,以便解决嵌套结果集的问题。想了解更多内容,请参考下面的关联元素。 |
name |
构造方法形参的名字。从 3.4.3 版本开始,通过指定具体的参数名,你可以以任意顺序写入 arg 元素。参看上面的解释。 |
2. 缓存
MyBatis 之中有一个查询缓存机制,可以缓存会话之中的数据。
要使用全局之中的二级缓存,只需要在 SQL 之中加入一行:
<cache/>
下面是来自https://blog.csdn.net/u012373815/article/details/47069223 的关于 MyBatis 的缓存的内容~
一级缓存是SqlSession级别的缓存。在操作数据库时需要构造 sqlSession对象,在对象中有一个(内存区域)数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。
一级缓存的作用域是同一个SqlSession,在同一个sqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。当一个sqlSession结束后该sqlSession中的一级缓存也就不存在了。Mybatis默认开启一级缓存。
二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession去操作数据库得到数据会存在二级缓存区域,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace
二级缓存的规则:
- 映射语句文件中的所有 select 语句的结果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
- 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
3. 动态 SQL
动态 SQL 可以摆脱需要拼接 SQL 语句的痛苦。
3.1 if
动态 SQL 要做的事根据条件来选择 包含where子句 的一部分,比如
<select id="findActiveBlogWithTitleLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
</select>
这条语句就提供了可选的查找功能,当 title 字段不为空的时候,不仅筛选所有状态为 active 的 blog, 还要对 title 进行模糊查找并且返回 BLOG 的结果。但是 title 字段如果为空,就不加后面的字段了。
多个条件筛选也没问题
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
3.2 choose, when,otherwise
类似于 if…else if…else if, 这样的选择结构体。上面 3.1 之中的 if 举例无法做筛选,只要对应字段有那么一定会被 拼接好。但是在这个结构体之中,可以对于条件做筛选,类似于 if…else if ….finally的写法。
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
3.3 trim, where,set
但是前面这两种还是不能解决下面的问题: 如果所有的 if 条件全都没有命中,那么会出现 SQL 语句语法错误的问题。比如:
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
如果就像之前讲的: 一个 if 条件都没有命中,那么最后拼接出来的 SQL 语句会变成:
SELECT * FROM BLOG
WHERE
那么这种语句不可以简单的用条件句式解决了。可以直接用下面的方法,自定义一个处理方式来解决。注意其中的 <where>
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>
<where>
会在至少有一个子元素的条件返回 SQL 子句的情况下,才去插入 where 语句。并且其会自动去除语句开头的 “AND” 或者 “OR”。
甚至在这个功能之中,我们可以自定义一个 where 的 trim 的处理方式。 比如,和自己默认的 where 元素等价的 自定义 trim 元素为:
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
此处要注意 prefixOverrides
之中的空格,其会忽略通过管道分割之中的文本序列,作用就是
- 移除指定在
prefixOverrides
之中的内容 - 插入
prefix
属性之中指定的内容
我们在本节的小标题之中还提到了:set,set 元素用于动态包含需要更新的列,逻辑筛选过程和之前提到的差不多。
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
where id=#{id}
</update>
这部分代码的功能我想大家都看明白了,也就是当传入的参数不为空的时候才对对应的列进行更新,其中<set>
就是起到更新功能的作用。
官方的说法是:set 元素会动态前置 SET 关键字,也会删掉无关的逗号。(比如最后一个 if 没有匹配上而产生的遗留逗号。)
set 等价的 trim 元素为:
<trim prefix="SET" suffixOverrides=",">
...
</trim>
注意和之前不同,这里是 suffixOverrides, 而不是之前的 prefixOverrides。 这个命令会删除之后的逗号。
3.4 foreach
foreach 的功能非常强大,其允许指定一个集合并且声明元素之内使用的 集合项(item) 和索引变量(index)。其也允许在元素之间添加分隔符。
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
3.5 bind
bind
可以从 OGNL 之中创建一个本来没有的变量,并且将其使用在上下文。比如:
<select id="selectBlogsLike" resultType="Blog">
<bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
SELECT * FROM BLOG
WHERE title LIKE #{pattern}
</select>
3.6 多数据库支持
很多情况下我们不同的数据使用的是不同种类的数据库,MyBatis 当然也支持对于不同种类数据库的分别处理,比如:
<selectKey keyProperty="id" resultType="int" order="BEFORE">
<if test="_databaseId == 'oracle'">
select seq_users.nextval from dual
</if>
<if test="_databaseId == 'db2'">
select nextval for seq_users from sysibm.sysdummy1"
</if>
</selectKey>
insert into users values (#{id}, #{name})
</insert>
上面的语句就分别对于 Oracle 数据库和 DB2 数据库进行不同的语句操作。