MyBatis学习09-XML映射器

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 缓存清除算法,可配置的值有以下四种:
  1. LRU – 最近最少使用:移除最长时间不被使用的对象。
  2. FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
  3. SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
  4. 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 类型进行执行。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值