深入浅出MyBatis-MapperBuilder

上一章节说道,解析mybatis的配置文件的最后一步是解析mapper元素。每个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" />

 

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

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

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

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

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

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

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

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

l        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-refcache都配置了,以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指带数据库表的列名。

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元素有很多属性允许配置,来决定每条语句的作用细节,如下:

属性

描述

id

在命名空间中唯一的标识符,可以被用来引用这条语句。同样,id+namespace既作为configuration中缓存的key又与namespace对应的接口中的方法关联。

parameterType

将会传入这条语句的参数类的完全限定名或别名。

parameterMap

已废弃。

resultType

从这条语句中返回的期望类型的类的完全限定名或别名。注意集合情形,那应该是集合可以包含的类型,而不能是集合本身(是否使用集合由id对应的方法的返回类型决定)。使用resultType或resultMap,但不能同时使用。

resultMap

引用外部的resultMap。使用resultMap或resultType,但不能同时使用。

flushCache

将其设置为true,不论语句什么时候被调用,都会导致缓存被清空。默认值:false。

useCache

将其设置为true,将会导致本条语句的结果被缓存。默认值:true。

timeout

这个设置驱动程序等待数据库返回请求结果,并抛出异常时间的最大等待值。默认不设置(驱动自行处理)。

fetchSize

这是暗示驱动程序每次批量返回的结果行数。默认不设置(驱动自行处理)。

statementType

STATEMENT,PREPARED或CALLABLE的一种。这会让MyBatis使用选择使用Statement,PreparedStatement或CallableStatement。默认值:PREPARED。

resultSetType

FORWARD_ONLY|SCROLL_SENSITIVE|SCROLL_INSENSITIVE中的一种。默认是不设置(驱动自行处理)。

 

2)       insertudpatedelete

	<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>

属性说明如下:

属性

描述

id

在命名空间中唯一的标识符,可以被用来引用这条语句。同样,id+namespace既作为configuration中缓存的key又与namespace对应的接口中的方法关联。

parameterType

将会传入这条语句的参数类的完全限定名或别名。

parameterMap

已废弃。

flushCache

将其设置为true,不论语句什么时候被调用,都会导致缓存被清空。默认值:false。

timeout

这个设置驱动程序等待数据库返回请求结果,并抛出异常时间的最大等待值。默认不设置(驱动自行处理)。

statementType

STATEMENT,PREPARED或CALLABLE的一种。这会让MyBatis使用选择使用Statement,PreparedStatement或CallableStatement。默认值:PREPARED。

useGeneratedKeys

(仅对insert有用)这会告诉MyBatis使用JDBC的getGeneratedKeys方法来取出由数据(比如:像MySQL和SQL Server这样的数据库管理系统的自动递增字段)内部生成的主键。默认值:false。

keyProperty

(仅对insert有用)标记一个属性,MyBatis会通过getGeneratedKeys或者通过insert语句的selectKey子元素设置它的值。默认:不设置。

 

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属性了。属性说明如下:

属性

描述

keyProperty

selectKey语句结果设置的目标属性。

resultType

结果的类型。MyBatis允许任何简单类型用作主键的类型,包括字符串。

order

这可以被设置为BEFORE或AFTER。如果设置为BEFORE,那么它会首先选择主键,设置keyProperty然后执行插入语句。如果设置为AFTER,那么先执行插入语句,然后是selectKey元素-这和如Oracle数据库相似,可以在插入语句中嵌入序列调用。

statementType

和前面的相同,MyBatis支持STATEMENT,PREPARED和CALLABLE语句的映射类型,分别代表PreparedStatement和CallableStatement类型。

 

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就用到了自动扫描,敬请期待)。

posted @ 2013-06-15 14:32  爱生活,爱编程  阅读(733)  评论(0编辑  收藏  举报