mybatis学习教程-5-Collection 元素

Collection 元素
<collection property="posts" ofType="domain.blog.Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<result property="body" column="post_body"/>
</collection>
collection 元素的作用差不多和 association 元素的作用一样。事实上,它们非常相似,以
至于再对相似点进行描述会显得冗余,因此我们只关注它们的不同点。
继续我们上面的例子,一个Blog 只有一个 Author。但一个 Blog 有许多帖子(文章)。在
Blog 类中,会像下面这样定义相应属性:
private List<Post> posts;
映射一个嵌套结果集到一个列表,我们使用collection 元素。就像 association 元素那
样,我们使用嵌套查询,或者从连接中嵌套结果集。
集合嵌套选择(Nested Select for Collection)
首先我们使用嵌套选择来加载 Blog 的文章。
<resultMap id=”blogResult” type=”Blog”>
<collection property="posts" javaType=”ArrayList” column="blog_id"
ofType="Post" select=”selectPostsForBlog”/>
</resultMap>
<select id=”selectBlog” parameterType=”int” resultMap=”blogResult”>
SELECT * FROM BLOG WHERE ID = #{id}
</select>
<select id=”selectPostsForBlog” parameterType=”int” resultType="Author">
SELECT * FROM POST WHERE BLOG_ID = #{id}
</select>
一看上去这有许多东西需要注意,但大部分看起与我们在association元素中学过的相似。首
先,您会注意到我们使用了collection元素,然后会注意到一个新的属性“ofType”。这个元素
是用来区别JavaBean属性(或者字段)类型和集合所包括的类型。因此您会读到下面这段代码。
<collection property="posts" javaType=”ArrayList” column="blog_id"
MyBatis 3 - User Guide
41
ofType="Post" select=”selectPostsForBlog”/>
� 理解为:
“一个名为 posts,类型为 Post 的 ArrayList 集合( A collection of
posts in an ArrayList of type Post)” 。
javaType 属性不是必须的,通常MyBatis 会自动识别,所以您通常可以简略地写成:
<collection property="posts" column="blog_id" ofType="Post"
select=”selectPostsForBlog”/>
集合的嵌套结果集(Nested Results for Collection)
这时候,您可能已经猜出嵌套结果集是怎样工作的了,因为它与association 非常相似,只
不过多了一个属性“ofType”。
让我们看下这个 SQL:
<select id="selectBlog" parameterType="int" 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>
同样,我们把Blog 和 Post 两张表连接在一起,并且也保证列标签名在映射的时候是唯一且
无歧义的。现在将 Blog 和 Post 的集合映射在一起是多么简单:
<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>
再次强调一下,id 元素是非常重要的。如果您忘了或者不知道id 元素的作用,请先读一下
上面 association 一节。
如果希望结果映射有更好的可重用性,您可以使用下面的方式:
<resultMap id="blogResult" type="Blog">
<id property=”id” column="blog_id" />
MyBatis 3 - User Guide
42
<result property="title" column="blog_title"/>
<collection property="posts" ofType="Post" resultMap=”blogPostResult”/>
</resultMap>
<resultMap id="blogPostResult" type="Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<result property="body" column="post_body"/>
</resultMap>
� Note:
在您的映射中没有深度、宽度、联合和集合数目的限制。但应该谨记,在进行映射的
时候也要考虑性能的因素。应用程序的单元测试和性能测试帮助您发现最好的方式可能要花很长
时间。但幸运的是,MyBatis 允许您以后可以修改您的想法,这时只需要修改少量代码就行了。
关于高级联合和集合映射是一个比较深入的课题,文档只能帮您了解到这里,多做一些实
践,一切将很快变得容易理解。
Discriminator 元素
<discriminator javaType="int" column="draft">
<case value="1" resultType="DraftPost"/>
</discriminator>
有时候一条数据库查询可能会返回包括各种不同的数据类型的结果集。Discriminator(识别
器)元素被设计来处理这种情况,以及其它像类继承层次情况。识别器非常好理解,它就像java
里的 switch 语句。
Discriminator定义要指定column和javaType属性。列是MyBatis 将要取出进行比较的值,
javaType 用来确定适当的测试是否正确运行(即使是String 在大部分情况下也可以工作),
例:
<resultMap id="vehicleResult" type="Vehicle">
<id property=”id” column="id" />
<result property="vin" column="vin"/>
<result property="year" column="year"/>
<result property="make" column="make"/>
<result property="model" column="model"/>
<result property="color" column="color"/>
<discriminator javaType="int" column="vehicle_type">
<case value="1" resultMap="carResult"/>
<case value="2" resultMap="truckResult"/>
<case value="3" resultMap="vanResult"/>
<case value="4" resultMap="suvResult"/>
</discriminator>
</resultMap>
在这个例子中,MyBatis 将会从结果集中取出每条记录,然后比较它的vehicle type 的值。
如果匹配任何 discriminator 中的 case,它将使用由 case 指定的resultMap。这是排它性的,换
句话说,其它的 case 的 resultMap 将会被忽略(除非使用我们下面说到的extended)。如果没
MyBatis 3 - User Guide
43
有匹配到任何 case,MyBatis 只是简单的使用定义在discriminator 块外面的 resultMap。所
以,如果 carResult 像下面这样定义:
<resultMap id="carResult" type="Car">
<result property="doorCount" column="door_count" />
</resultMap>
那么,只有doorCount属性会被加载。这样做是为了与识别器cases群组完全独立开来,哪怕
它与上一层的resultMap 一点关系都没有。在刚才的例子里我们当然知道cars和vehicles的关
系,a Car is-a Vehicle。因此,我们也要把其它属性加载进来。我们要稍稍改动一下
resultMap:
<resultMap id="carResult" type="Car" extends=”vehicleResult”>
<result property=”doorCount” column="door_count" />
</resultMap>
现在,vehicleResult 和 carResult 的所有属性都会被加载。
可能有人会认为这样扩展映射定义有一点单调了,所以还有一种可选的更加简单明了的映射
风格语法。例如:
<resultMap id="vehicleResult" type="Vehicle">
<id property=”id” column="id" />
<result property="vin" column="vin"/>
<result property="year" column="year"/>
<result property="make" column="make"/>
<result property="model" column="model"/>
<result property="color" column="color"/>
<discriminator javaType="int" column="vehicle_type">
<case value="1" resultType="carResult">
<result property=”doorCount” column="door_count" />
</case>
<case value="2" resultType="truckResult">
<result property=”boxSize” column="box_size" />
<result property=”extendedCab” column="extended_cab" />
</case>
<case value="3" resultType="vanResult">
<result property=”powerSlidingDoor” column="power_sliding_door" />
</case>
<case value="4" resultType="suvResult">
<result property=”allWheelDrive” column="all_wheel_drive" />
</case>
</discriminator>
</resultMap>
� 记住:对于这么多的结果映射,如果您不指定任何的结果集,那么MyBatis 会自动地将列名
与属性相匹配。所以上面所举的例子比实际中需要的要详细。尽管如此,大部分数据库有点复
杂,并且它并不是所有情况都是完全可以适用的。
Cache 元素
MyBatis 包含一个强大的、可配置、可定制的查询缓存机制。MyBatis 3 的缓存实现有了许
MyBatis 3 - User Guide
44
多改进,使它更强大更容易配置。默认的情况,缓存是没有开启的,除了会话缓存以外,会话缓
存可以提高性能,且能解决循环依赖。开启二级缓存,您只需要在SQL映射文件中加入简单的一
行:
<cache/>
这句简单的语句作用如下:

所有映射文件里的 select 语句的结果都会被缓存。

所有映射文件里的 insert、 update 和 delete 语句执行都会清空缓存 。

缓存使用最近最少使用算法(LRU)来回收。

缓存不会被设定的时间所清空。

每个缓存可以存储 1024 个列表或对象的引用(不管查询方法返回的是什么)。

缓存将作为“读/写”缓存,意味着检索的对象不是共享的且可以被调用者安全地修改,
而不会被其它调用者或者线程干扰。
所有这些特性都可以通过 cache 元素进行修改。例如:
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
这种高级的配置创建一个每60秒刷新一次的FIFO 缓存,存储512个结果对象或列表的引用,
并且返回的对象是只读的。因此在不用的线程里的调用者修改它们可能会引用冲突。
可用的回收算法如下:

LRU – 最近最少使用:移出最近最长时间内都没有被使用的对象。

FIFO –先进先出:移除最先进入缓存的对象。

SOFT – 软引用: 基于垃圾回收机制和软引用规则来移除对象(空间内存不足时才进行回
收)。

WEAK – 弱引用:
基于垃圾回收机制和弱引用规则(垃圾回收器扫描到时即进行回
收)。
MyBatis 3 - User Guide
45
默认使用 LRU。
flushInterval :设置任何正整数,代表一个以毫秒为单位的合理时间。默认是没有设置,因此
没有刷新间隔时间被使用,在语句每次调用时才进行刷新。
Size:属性可以设置为一个正整数,您需要留意您要缓存对象的大小和环境中可用的内存空间。
默认是1024。
readOnly:属性可以被设置为true 或false。只读缓存将对所有调用者返回同一个实例。因此这
些对象都不能被修改,这可以极大的提高性能。可写的缓存将通过序列化来返回一个缓存对象的
拷贝。这会比较慢,但是比较安全。所以默认值是false。
使用自定义缓存
除了上面已经定义好的缓存方式,您能够通过您自己的缓存实现来完全重写缓存行为,或者
通过创建第三方缓存解决方案的适配器。
<cache type=”com.domain.something.MyCustomCache”/>
这个例子演示了如果自定义缓存实现。由 type 指定的类必须实现 org.mybatis.cache.Cache
接口。这个接口是 MyBatis 框架比较复杂的接口之一,先给个示例:
public interface Cache {
String getId();
int getSize();
void putObject(Object key, Object value);
Object getObject(Object key);
boolean hasKey(Object key);
Object removeObject(Object key);
void clear();
ReadWriteLock getReadWriteLock();
}
要配置您的缓存,简单地添加一个公共的JavaBeans 属性到您的缓存实现中,然后通过cache
元素设置属性进行传递,下面示例,将在您的缓存实现上调用一个setCacheFile(String file)方
法。
<cache type=”com.domain.something.MyCustomCache”>
<property name=”cacheFile” value=”/tmp/my-custom-cache.tmp”/>
</cache>
您可以使用所有简单的JavaBeans 属性,MyBatis 会自动进行转换。
MyBatis 3 - User Guide
46
需要牢记的是一个缓存配置和缓存实例都绑定到一个SQL Map 文件命名空间。因此,所有的
这个相同命名空间的语句也都和这个缓存绑定。语句可以修改如何与这个缓存相匹配,或者使用
两个简单的属性来完全排除它们自己。默认情况下,语句像下面这样来配置:
<select ... flushCache=”false” useCache=”true”/>
<insert ... flushCache=”true”/>
<update ... flushCache=”true”/>
<delete ... flushCache=”true”/>
因为有默认值,所以您不需要使用这种方式明确地配置这些语句。如果您想改变默认的动
作,只需要设置flushCache和useCache 属性即可。举个例子来说,在许多的场合下您可能排除缓
存中某些特定的select语句。或者您想用select语句清空缓存。同样的,您也可能有一些update
语句在执行的时候不需要清空缓存。
cache-ref 元素
回想上一节,我们仅仅只是讨论在某一个命名空间里使用或者刷新缓存。但有可能您想要在
不同的命名空间里共享同一个缓存配置或者实例。在这种情况下,您就可以使用cache-ref 元素
来引用另外一个缓存。
<cache-ref namespace=”com.someone.application.data.SomeMapper”/>

相关推荐