MyBatis学习09-XML映射器
- 1 概述
- 2 配置文件头部声明
- 3 cache(缓存)
- 4 cache-ref(缓存引用)
- 5 select(查询标签)
- 6 insert/update/delte(增、删、改标签)
- 6 sql(SQL标签)
- 7 resultMap(结果集映射)
- 8 其他补充
- 8.1 #{} 与 ${} 的区别
- 8.2 resultType 与 resultMap 的区别
1 概述
MyBatis 中的 XML 映射器指的是专门存放 SQL 语句的 XML 配置文件。MyBatis 的真正强大在于它的语句映射,如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。
SQL 映射文件只有很少的几个顶级元素(按照应被定义的顺序列出):
- cache(缓存) – 该命名空间的缓存配置。
- cache-ref(缓存引用) – 引用其它命名空间的缓存配置。
- resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
- sql – 可被其它语句引用的可重用语句块。
- insert – 映射插入语句。
- update – 映射更新语句。
- delete – 映射删除语句。
- select – 映射查询语句。
2 配置文件头部声明
在 XML 映射器文件中,需要进行头部的声明,声明内容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3 cache(缓存)
MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:
<cache/>
基本上就是这样。这个简单语句的效果如下:
- 映射语句文件中的所有 select 语句的结果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
- 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
cache 标签可配置的属性有:
- eviction 缓存清除算法,可配置的值有以下四种:
- LRU – 最近最少使用:移除最长时间不被使用的对象。
- FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
- SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
- WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
- flushInterval 缓存刷新间隔,属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。
- size 可缓存的引用数目,属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。
- readOnly 只读,属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。
虽然 MyBatis 提供了自定义缓存的功能,但由于现在第三方的缓存都很成熟,且各个业务上对于缓存的使用与设计都是不一样的,所以 MyBatis 在这样的环境下,一二级缓存的设计有点鸡肋(对小而快的系统还是挺有用的)。
4 cache-ref(缓存引用)
可以通过 cache-ref 标签引用其它命名空间中的缓存配置,即达到多个命名空间中共享相同的缓存配置和实例。示例如下:
<cache-ref namespace="com.someone.application.data.SomeMapper"/>
5 select(查询标签)
数据库的查询操作需要使用 select 标签,即所有的 select 语句都必须写在 select 标签中,一个简单的查询示例如下:
<select id="selectPerson" parameterType="int" resultType="hashmap">
SELECT * FROM PERSON WHERE ID = #{id}
</select>
-
select
表示这是一个查询语句。 -
id="selectPerson"
表示该标签的唯一标识,selectPerson
与 Java 接口中的 selectPerson 方法一一匹配。PS:该值不允许重复,否则会报错。 -
parameterType="int"
表示入参是 int 类型。 -
resultType="hashmap"
表示返回的结果集为 HaspMap。 -
#{id}
表示参数为 id,其中 # 代表占位符,与 JDBC 中的 ? 功能一致。 -
SELECT * FROM PERSON WHERE ID = #{id}
表示要执行的 SQL 语句。
select 标签不只有 id、parameterType、resultType 属性,项目上常用的属性如下表格所示:
属性 | 描述 |
id | 在命名空间中唯一的标识符,可以被用来引用这条语句。 |
parameterType | 将会传入这条语句的参数的类全限定名或别名。这个属性是可选的, 因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体 传入语句的参数,默认值为未设置(unset)。 |
resultType | 期望从这条语句中返回结果的类全限定名或别名。注意,如果返回的 是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。 |
resultMap | 对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性, 如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。 |
flushCache | 将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存 被清空,默认值:false。 |
useCache | 将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来, 默认值:对 select 元素为 true。 |
timeout | 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒 数。默认值为未设置(unset)(依赖数据库驱动)。 |
6 insert/update/delte(增、删、改标签)
增、删、改标签与查询标签的属性差不多,增、删、改的示例代码如下:
<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 可以通过类型处理器(TypeHandler)推断出具体 传入语句的参数,默认值为未设置(unset)。 |
flushCache | 将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存 被清空,默认值:false。 |
timeout | 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒 数。默认值为未设置(unset)(依赖数据库驱动)。 |
6 sql(SQL标签)
项目开发过程中,可能会有多个 SQL 的查询列一样,使用 SQL 标签可以解决相同代码在多处出现的问题,sql 标签用法如下:
<sql id="userColumns">
id, username, password
</sql>
<select id="selectUsers" resultType="User">
select
<include refid="userColumns">
from users
where id = #{id}
</select>
7 resultMap(结果集映射)
在使用 select 标签时,如果返回属性使用的是 resultType,在 MyBatis 的后台自自动将 resultType 转换成 resultMap,即所有的查询语句最终都是调用的 resultMap 相关的逻辑。
resultMap 的简单示例如下:
<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="username" column="user_name"/>
<result property="password" column="hashed_password"/>
</resultMap>
<select id="selectUsers" resultMap="userResultMap">
select user_id, user_name, hashed_password
from some_table
where id = #{id}
</select>
其上面的 resultMap 等价于下面示例代码:
<select id="selectUsers" resultType="User">
select
user_id as "id",
user_name as "userName",
hashed_password as "hashedPassword"
from some_table
where id = #{id}
</select>
## 在 mybatis-config.xml 中如果配置了
<setting name="mapUnderscoreToCamelCase" value="true" />
则上面的 as "id", as "uuserName", as "hashedPassword" 可以省略
resultMap 有很多的子元素及对应的属性,且可嵌套使用,过于复杂,但常用的 resultMap 示例代码如下:
<resultMap id="blogResult" type="Blog">
<id property="id" column="blog_id" />
<result property="title" column="blog_title"/>
<association property="author" javaType="Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
<result property="password" column="author_password"/>
<result property="email" column="author_email"/>
<result property="bio" column="author_bio"/>
</association>
<collection property="posts" ofType="Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<result property="body" column="post_body"/>
</collection>
</resultMap>
其中标签及属性说明如下:
- id : 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
- result : 注入到字段或 JavaBean 属性的普通结果
- association : 一对一关系查询时使用的标签
- property : 映射到列结果的字段或属性
- javaType : 一个 Java 类的完全限定名,或一个类型别名
- select : 关联的嵌套 select 查询,该属性尽量不要使用,会存在 N+1 的问题
- collection : 一对多关系查询时使用的标签
association 的使用示例如下:
<select id="selectBlog" resultMap="blogResult">
select
B.id as blog_id,
B.title as blog_title,
B.author_id as blog_author_id,
A.id as author_id,
A.username as author_username,
A.password as author_password,
A.email as author_email,
A.bio as author_bio
from Blog B left outer join Author A on B.author_id = A.id
where B.id = #{id}
</select>
<resultMap id="blogResult" type="Blog">
<id property="id" column="blog_id" />
<result property="title" column="blog_title"/>
<association property="author" javaType="Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
<result property="password" column="author_password"/>
<result property="email" column="author_email"/>
<result property="bio" column="author_bio"/>
</association>
</resultMap>
collection 的使用示例如下:
<select id="selectBlog" resultMap="blogResult">
select
B.id as blog_id,
B.title as blog_title,
B.author_id as blog_author_id,
P.id as post_id,
P.subject as post_subject,
P.body as post_body,
from Blog B
left outer join Post P on B.id = P.blog_id
where B.id = #{id}
</select>
<resultMap id="blogResult" type="Blog">
<id property="id" column="blog_id" />
<result property="title" column="blog_title"/>
<collection property="posts" ofType="Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<result property="body" column="post_body"/>
</collection>
</resultMap>
8 其他补充
8.1 #{} 与 ${} 的区别
- #{} 表示占位符,与 JDBC 中的 ? 功能一致,示例如下:
SELECT * FROM PERSON WHERE ID = #{id}
// 近似的 JDBC 代码,非 MyBatis 代码...
String selectPerson = "SELECT * FROM PERSON WHERE ID=?";
PreparedStatement ps = conn.prepareStatement(selectPerson);
ps.setInt(1,id);
- ${} 表示字符串替换,与 Java 中的字符串拼接功能一致,示例如下:
SELECT * FROM PERSON WHERE ${idSql}
// 近似的 JDBC 代码,非 MyBatis 代码...
String selectPerson = "SELECT * FROM PERSON ";
String idSql = " WHERE ID = 1 ";
PreparedStatement ps = conn.prepareStatement(selectPerson + idSql);
8.2 resultType 与 resultMap 的区别
- resultType 表示返回的是原始数据类型或者 JavaBean 对象。
- resultMap 表示返回的 MyBatis 的 resultMap 标签中的结果列,该结果列本质由 resultType 类型组合而成。所有的 resultType 的执行在最终过程都是转换成 resultMap 类型进行执行。