MyBatis - MapperBuilder

上一章节说道,解析mybatis的配置文件的最后一步是解析mapper元素。每个mapper元素对应一个mapper配置文件,这一章节就来讲讲这个mapper配置文件是如何解析的。mapper配置文件是配置sql映射的地方,它看起来就像下面这个样子:

<?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">
<mapper namespace="org.iMybatis.abc.dao.UserDao">
	<cache type="PERPETUAL" eviction="LRU" flushInterval="60000"
		size="512" readOnly="true" />
	<resultMap id="userResultMap" type="UserDto">
		<id property="userid" column="userid" />
		<result property="username" column="username" />
		<result property="password" column="password" />
	</resultMap>
	<sql id="userColumns"> userid,username,password </sql>
	<select id="queryUsers" parameterType="UserDto" resultType="UserDto"
		useCache="false">
		select <include refid="userColumns" />
		from t_user t where t.username = #{username}
	</select>
	<insert id="insertUser" parameterType="UserDto"
		useGeneratedKeys="true" keyProperty="userid">
		insert into t_user (userid,username,password)
		values (#{userid},#{username},#{password})
	</insert>
	<update id="updateUser" parameterType="UserDto">
		update t_user set
		username= #{username},
		password = #{password},
		where userid = #{userid}
	</update>
	<delete id="deleteUser" parameterType="UserDto">
		delete from t_user where userid = #{userid}
	</delete>
</mapper>

Mapper元素只有一个属性namespace,它有两个作用:一是用于区分不同的mapper(在不同的mapper文件里,子元素的id可以相同,mybatis通过namespace和子元素的id联合区分),二是与接口关联(应用程序通过接口访问mybatis时,mybatis通过接口的完整名称查找对应的mapper配置,因此namespace的命名务必小心一定要某接口同名)。此外,mapper配置文件还有几个顶级子元素(它们须按照顺序定义):

l cache:配置本地命名空间的缓存。

l cache-ref:从其他命名空间引用缓存配置。

l resultMap:结果映射,用来描述如何从数据库结果集映射到你想要的对象。

l parameterMap:已经被废弃了!建议不要使用,本章也不会对它进行讲解。

l sql:可以重用的SQL块,可以被其他数据库操作语句引用。

l insert | update | delete | select:数据库操作语句。

Mapper的解析在XMLMapperBuilder里完成,主要通过configurationElement方法完成解析,在configurationElement内部调用各个元素的子解析方法完成解析。

下面分别介绍这些子元素。
 

一、cache

1、配置示例

<cache type="PERPETUAL" eviction="LRU"flushInterval="60000" size="512"readOnly="true" />

type-cache:实现类,默认为PERPETUAL,可以使用自定义的cache实现类(别名或完整类名皆可)

eviction:回收算法,默认为LRU,可选的算法有:

  LRU:最近最少使用的:移除最长时间不被使用的对象。

  FIFO:先进先出:按对象进入缓存的顺序来移除它们。

  SOFT:软引用:移除基于垃圾回收器状态和软引用规则的对象。

  WEAK:弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

flushInterval:刷新间隔,默认为1个小时,单位毫秒

size:缓存大小,默认大小1024,单位为引用数

readOnly:只读

2、解析

cacheElement方法负责解析cache元素,它通过调用CacheBuilder的相应方法完成cache的创建。每个cache内部都有一个唯一的ID,这个id的值就是namespace。创建好的cache对象存入configuration的cache缓存中(该缓存以cache的ID属性即namespace为key,这里再次体现了mybatis的namespace的强大用处)。

二、cache-ref

1、配置示例

<cache-ref namespace="com.someone.application.data.SomeMapper"/>

如果你不想定义自己的cache,可以使用cache-ref引用别的cache。因为每个cache都以namespace为id,所以cache-ref只需要配置一个namespace属性就可以了。需要注意的是,如果cache-ref和cache都配置了,以cache为准。

2、解析

cacheRefElement方法负责解析cache-ref元素,它通过调用CacheRefResolver的相应方法完成cache的引用。创建好的cache-ref引用关系存入configuration的cacheRefMap缓存中。

三、resultMap

1、配置示例

<resultMap id="userResultMap" type="User">
    <id property=" userid " column="userid" />
    <result property="username" column="username" />
    <result property="password" column="password" />
</resultMap>

resultMap提供了从数据库表列名到java对象属性的映射管理,示例只是提供了最简单的情况,resultMap能做的不仅仅是示例中这么点(作者不喜欢使用resultMap完成结果的复杂映射,相反更喜欢结果和对象的直接映射)。在mapper配置文件中可以配置多个resultMap,不同的resultMap用id加以区分。type属性标记java类型(别名)。子元素中的property指带java中的属性,column指带数据库表的列名。

其实 id 跟 result 标签可以通用,只是 id 有特殊标识可以在影射的时候更快的匹配到,如果我没猜错的话这个 id 标识其实不是主键也能用。

2、解析

resultMapElement方法负责解析resultMap元素,它通过调用ResultMapResolver的相应方法完成resultMap的解析。创建好的resultMap存入configuration的resultMaps缓存中(该缓存以namespace+resultMap的id为key,这里再次体现了mybatis的namespace的强大用处)。

四、sql

1、配置示例

<sql id="userColumns"> userid,username,password</sql>

这个元素可以被用来定义可重用的SQL代码段,可以包含在其他语句中。例如:

<select id="queryUsers" parameterType="UserDto" resultType="UserDto" useCache="false">
    select <include refid="userColumns"/> from t_user t where t.username = #{username}
</select>

 2、解析

sqlElement方法负责解析sql元素。id属性用于区分不同的sql元素,在同一个mapper配置文件中可以配置多个sql元素。

五、statement

1、配置示例

作为数据库访问的statement元素包含4种类型:insert、update、delete、query。其中前3者可以归为一类,query单独为一类,下面分开讲解。

(1)select

<select id="queryUsers" parameterType="UserDto" resultType="UserDto" useCache="false">
    select <include refid="userColumns"/> from t_user t where t.username = #{username}
</select>

select元素有很多属性允许配置,来决定每条语句的作用细节,如下:

(2)insert、udpate、delete

<insert id="insertUser" parameterType="UserDto" useGeneratedKeys="true" keyProperty="userid">
    insert into t_user (userid,username,password)
    values (#{userid},#{username},#{password})
</insert>

<update id="updateUser" parameterType="UserDto">
    update t_user set
    username = #{username},
    password = #{password},
    where userid = #{userid}
</update>

<delete id="deleteUser" parameterType="UserDto">
    delete from t_user where userid = #{userid}
</delete>

属性说明如下:

(3)selectKey

<selectKey keyProperty="userid" resultType="int" order="BEFORE">
     select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1
</selectKey>

selectKey给了你一个简单的行为在你的数据库中来处理自动生成的主键,而不需要使你的Java代码变得复杂。在上面的示例中,selectKey元素将会首先运行,userid会被设置,然后插入语句会被调用。另外,selectKey节点生成的KeyGenerator优先级高于statement节点的useGeneratedKeys属性生成的KeyGenerator对象,也就是说配置了SelectKey子节点就不需要再配置useGeneratedKeys属性了。属性说明如下:

2、解析

buildStatementFromContext方法负责解析statement元素。id属性用于区分不同的statement元素,在同一个配置文件中可以配置多个statement元素。通过调用XMLStatementBuilder的parseStatementNode方法完成解析。在这个方法内有几个重要的步骤,理解他们对正确的配置statement元素很有帮助。

(1)动态解析子元素

statement节点可以配置各种子元素,比如前面提到的include子元素和selectKey子元素等(在动态sql里还有更多的子元素,具体参考mybatis的官方文档)。动态解析子元素通过parseDynamicTags方法完成。该方法根据子元素的类型递归的解析成一个个的SqlNode,这些SqlNode对象提供了apply方法,供后续调用时生成sql语句所需。需要注意的是SelectKey没有对应的SqlNode对象,因为它的功能是用来生成KeyGenerator对象的(具体来说是SelectKeyGenerator对象)。另外,SelectKey节点生成的KeyGenerator优先级高于statement节点的useGeneratedKeys属性生成的KeyGenerator对象,也就是说配置了SelectKey子节点就不需要再配置useGeneratedKeys属性了。

(2)生成SqlSource

SqlSource用于后续调用时根据SqlNode和参数对象生成sql语句。它接收一个叫做rootsqlNode的对象作为构造参数。

(3)生成KeyGenerator

如果配置了selectKey子元素,KeyGenerator直接使用selectKey子元素里生成的KeyGenerator对象(具体来说是SelectKeyGenerator对象)。若没配置,则如果useGeneratedKeys属性的值为"true"且配置了 keyProperty属性,则生成默认的Jdbc3KeyGenerator对象,该对象调用JDBC驱动的getGeneratedKeys方法返回insert语句执行后生成的自增长主键。

(4)创建MappedStatement

MappedStatement对象封装了statement元素的所有属性以及子节点值,MappedStatement对象有一个id属性用于唯一标记它,这个id由namespace加statement元素的id属性值构成。创建好的MappedStatement对象存入Configuration对象的mappedStatements缓存中,key为MappedStatement对象的id值。

附:注册mapper类型

我们知道每个mapper配置文件的namespace属性对应于某个接口,应用程序通过接口访问mybatis时,mybatis会为这个接口生成一个代理对象,这个对象就叫mapper对象,在生成代理对象前mybatis会校验接口是否已注册,未注册的接口会产生一个异常。为了避免这种异常,就需要注册mapper类型。这个步骤是在XMLMapperBuilder的bindMapperForNamespace方法中完成的。它通过调用Configuration对象的addMapper方法完成,而Configuration对象的addMapper方法是通过MapperRegistry的addMapper方法完成的,它只是简单的将namespace属性对应的接口类型存入本地缓存中。

Configuration对象提供了一个重载的addMappers(StringpackageName)方法,该方法以包路径名为参数,它的功能是自动扫描包路径下的接口并注册到MapperRegistry的缓存中,同时扫描包路径下的mapper配置文件并解析之。解析配置文件是在MapperAnnotationBuilder类的parse方法里完成的,该方法先解析配置文件,然后再解析接口里的注解配置,且注解里的配置会覆盖配置文件里的配置,也就是说注解的优先级高于配置文件,这点需要注意。采用自动扫描会大大简化配置,只不过需要应用程序自己调用,mybatis默认是不会调用这个方法的(后续将会讲解的spring集成mybatis就用到了自动扫描,敬请期待)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值