mybatis之ResultMap,延迟加载和MySQL数据类型转换

1 mybatis标签

1.1 ResultMap

示例如下:

<resultMap id="detailedBlogResultMap" type="Blog">  
<constructor>  
<idArg column="blog_id" javaType="int"/>  
</constructor>  
<result property="title" column="blog_title"/>  
<association property="author" column="blog_author_id" 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"/>  
<result property="favouriteSection" column="author_favourite_section"/>  
</association>  
<collection property="posts" ofType="Post">  
<id property="id" column="post_id"/>  
<result property="subject" column="post_subject"/>  
<association property="author" column="post_author_id" javaType="Author"/>  
<collection property="comments" column="post_id" ofType=" Comment">  
<id property="id" column="comment_id"/>  
</collection>  
<collection property="tags" column="post_id" ofType=" Tag" >  
<id property="id" column="tag_id"/>  
</collection>  
<discriminator javaType="int" column="draft">  
<case value="1" resultType="DraftPost"/>  
</discriminator>  
</collection>  
</resultMap>  

1.1.1 id, result元素

<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>

这是最基本的结果集映射。idresult 将列映射到属性或简单的数据类型字段(String, int, double, Date等)。
这两者唯一不同的是,在比较对象实例时id 作为结果集的标识属性。这有助于提高总体性能,特别是应用缓存和嵌套结果映射的时候。

Id、result属性如下:

AttributeDescription
property映射数据库列的字段或属性。如果JavaBean的属性与给定的名称匹配,就会使用匹配的名字。否则,MyBatis将搜索给定名称的字段。两种情况下您都可以使用逗点的属性形式。比如,可以映射到“username”,也可以映射到“address.street.number”
column数据库的列名或者列标签别名。与传递给resultSet.getString(columnName)的参数名称相同
javaType完整java类名或别名(参考上面的内置别名列表)。如果映射到一个JavaBean,那MyBatis 通常会自行检测到。然而,如果映射到一个HashMap,那应该明确指定javaType 来确保所需行为
jdbcType这张表下面支持的JDBC类型列表列出的JDBC类型。这个属性只在insert,update或delete 的时候针对允许空的列有用。JDBC 需要这项,但MyBatis 不需要。如果直接编写JDBC代码,在允许为空值的情况下需要指定这个类型
typeHandler使用这个属性可以重写默认类型处理器。它的值可以是一个TypeHandler实现的完整类名,也可以是一个类型别名

1.1.2 Constructor元素

<constructor>
<idArg column="id" javaType="int"/>
<arg column=”username” javaType=”String”/>
</constructor>

当属性与DTO,或者与自己的域模型一起工作的时候,许多场合要用到不变类。通常,包含引用,或者查找的数据很少或者数据不会改变的的表,适合映射到不变类中。构造器注入允许在类实例化后给类设值,这不需要通过public方法。MyBatis同样也支持private属性和JavaBeans的私有属性达到这一点,构造器元素可以做到这点

AttributeDescription
column数据库的列名或者列标签别名。与传递给resultSet.getString(columnName)的参数名称相同
javaType完整java类名或别名(参考上面的内置别名列表)。如果映射到一个JavaBean,那MyBatis 通常会自行检测到。然而,如果映射到一个HashMap,那您应该明确指定javaType 来确保所需行为
jdbcType支持的JDBC类型列表中列出的JDBC类型。这个属性只在insert,update 或delete 的时候针对允许空的列有用。JDBC 需要这项,但MyBatis 不需要。如果您直接编写JDBC代码,在允许为空值的情况下需要指定这个类型
typeHandler使用这个属性可以重写默认类型处理器。它的值可以是一个TypeHandler实现的完整类名,也可以是一个类型别名

1.1.3 association元素

<association property="author" column="blog_author_id" javaType=" Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
</association>

Association元素处理has-one(一对一)这种类型关系。比如在我们的例子中,一个Blog有一个Author。联合映射与其它的结果集映射工作方式差不多,指定property、column、javaType(通常MyBatis会自动识别)、jdbcType(如果需要)、typeHandler。
不同的地方是需要告诉MyBatis如何加载一个联合查询。MyBatis使用两种方式来加载:

  • Nested Select : 通过执行另一个返回预期复杂类型映射SQL语句(即引用外部定义好的SQL语句块)
  • Nested Results : 通过嵌套结果映射(nested result mappings)来处理联接结果集(joined results)的重复子集。

首先,让我们检查一下元素属性。正如看到的,它不同于普通只有selectresultMap属性的结果映射

AttributeDescription
property映射数据库列的字段或属性。如果JavaBean 的属性与给定的名称匹配,就会使用匹配的名字。否则,MyBatis 将搜索给定名称的字段。两种情况下您都可以使用逗点的属性形式。比如,您可以映射到”username”,也可以映射到更复杂点的”address.street.number”
column数据库的列名或者列标签别名。与传递给resultSet.getString(columnName)的参数名称相同。注意: 在处理组合键时,您可以使用column= “{prop1=col1,prop2=col2}”这样的语法,设置多个列名传入到嵌套查询语句。这就会把prop1和prop2设置到目标嵌套选择语句的参数对象中
javaType完整java类名或别名(参考上面的内置别名列表)。如果映射到一个JavaBean,那MyBatis 通常会自行检测到。然而,如果映射到一个HashMap,那应该明确指定javaType 来确保所需行为
jdbcType支持的JDBC类型列表中列出的JDBC类型。这个属性只在insert,update 或delete 的时候针对允许空的列有用。JDBC 需要这项,但MyBatis 不需要。如果直接编写JDBC代码,在允许为空值的情况下需要指定这个类型
typeHandler使用这个属性可以重写默认类型处理器。它的值可以是一个TypeHandler实现的完整类名,也可以是一个类型别名
select通过这个属性,通过ID引用另一个加载复杂类型的映射语句。从指定列属性中返回的值,将作为参数设置给目标select 语句。表格下方将有一个例子。注意:在处理组合键时,可以使用column=”{prop1=col1,prop2=col2}”这样的语法,设置多个列名传入到嵌套语句。这就会把prop1和prop2设置到目标嵌套语句的参数对象中
resultMap一个可以映射联合嵌套结果集到一个适合的对象视图上的ResultMap 。这是一个替代的方式去调用另一个select 语句。它允许你去联合多个表到一个结果集里。这样的结果集可能包括冗余的、重复的需要分解和正确映射到一个嵌套对象视图的数据组。简言之,MyBatis让你把结果映射‘链接’到一起,用来处理嵌套结果

使用select例子:

<resultMap id=”blogResult” type=”Blog”>  
<association property="author" column="blog_author_id" javaType="Author"  
select=”selectAuthor”/>  
</resultMap>  
   
<select id=”selectBlog” parameterType=”int” resultMap=”blogResult”>  
SELECT * FROM BLOG WHERE ID = #{id}  
</select>  
   
<select id=”selectAuthor” parameterType=”int” resultType="Author">  
SELECT * FROM AUTHOR WHERE ID = #{id}  
</select>

使用resultmap例子:

查询语句
<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,  
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=”blog_id” column="id" />  
<result property="title" column="blog_title"/>  
<association property="author" column="blog_author_id" javaType="Author"  
resultMap=”authorResult”/>  
</resultMap>  
   
<resultMap id="authorResult" type="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"/>  
</resultMap>  

在上面的例子中,看到Blog的作者(“author”)联合一个“authorResult”结果映射来加载Author实例。
重点提示:id元素在嵌套结果映射中扮演了非常重要的角色,应该总是指定一个或多个属性来唯一标识这个结果集。事实上,如果没有那样做,MyBatis也会工作,但是会导致严重性能开销。选择尽量少的属性来唯一标识结果,而使用主键是最明显的选择(即使是复合主键)

集合的嵌套结果集(Nested Results for Collection)
上面的例子使用一个扩展的resultMap 元素来联合映射。这可使Author结果映射可重复使用。然后,如果不需要重用它,可以直接嵌套这个联合结果映射。下面例子就是使用这样的方式:

<resultMap id="blogResult" type="Blog">  
<id property=”blog_id” column="id" />  
<result property="title" column="blog_title"/>  
<association property="author" column="blog_author_id" 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>

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

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

如果希望结果映射有更好的可重用性,您可以使用下面的方式

<resultMap id="blogResult" type="Blog">  
<id property=”id” column="blog_id" />  
<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> 

1.1.5 Discriminator元素

<discriminator javaType="int" column="draft">
<case value="1" resultType="DraftPost"/>
</discriminator>

有时候一条数据库查询可能会返回包括各种不同的数据类型的结果集。Discriminator(识别器)元素被设计来处理这种情况,以及其它像类继承层次情况。识别器非常好理解,它就像java里的switch语句。

Discriminator定义要指定columnjavaType属性。列是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。这是排它性的,换句话说,其它的caseresultMap将会被忽略(除非使用我们下面说到的extended)。如果没有匹配到任何caseMyBatis只是简单的使用定义在discriminator块外面的resultMap。所以,如果carResult像下面这样定义:

<resultMap id="carResult" type="Car">
<result property=”doorCount” column="door_count" />
</resultMap>

那么,只有doorCount属性会被加载。这样做是为了与识别器cases群组完全独立开来,哪怕它与上一层的resultMap一点关系都没有。在刚才的例子里我们当然知道carsvehicles的关系,a Car is-a Vehicle。因此,我们也要把其它属性加载进来。我们要稍稍改动一下resultMap,使用extends

<resultMap id="carResult" type="Car" extends=”vehicleResult”>
<result property=”doorCount” column="door_count" />
</resultMap>

现在,vehicleResultcarResult的所有属性都会被加载。
可能有人会认为这样扩展映射定义有一点单调了,所以还有一种可选的更加简单明了的映射风格语法。例如:

<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会自动地将列名与属性相匹配。所以上面所举的例子比实际中需要的要详细

1.1.6 Cache元素

MyBatis包含一个强大的、可配置、可定制的查询缓存机制。MyBatis 3 的缓存实现有了许多改进,使它更强大更容易配置。默认的情况,缓存是没有开启,除了会话缓存以外,它可以提高性能,且能解决循环依赖。开启二级缓存,只需要在SQL映射文件中加入简单的一行:

<cache/>

这句简单的语句作用如下:

  • 所有映射文件里的select语句的结果都会被缓存。
  • 所有映射文件里的insert、update和delete语句执行都会清空缓存。
  • 缓存使用最近最少使用算法(LRU)来回收。
  • 缓存不会被设定的时间所清空。
  • 每个缓存可以存储1024个列表或对象的引用(不管查询方法返回的是什么)。
  • 缓存将作为读/写缓存,意味着检索的对象不是共享的且可以被调用者安全地修改,而不会被其它调用者或者线程干扰。

所有这些特性都可以通过cache元素进行修改。例如:

<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>

这种高级的配置创建一个每60秒刷新一次的FIFO缓存,存储512个结果对象或列表的引用,并且返回的对象是只读的。因此在不用的线程里的调用者修改它们可能会引用冲突。
标签内一些元素:

  • flushInterval:设置任何正整数,代表一个以毫秒为单位的合理时间。默认是没有设置,因此没有刷新间隔时间被使用,在语句每次调用时才进行刷新。
  • Size:属性可以设置为一个正整数,您需要留意您要缓存对象的大小和环境中可用的内存空间。默认是1024。
  • readOnly:属性可以被设置为truefalse。只读缓存将对所有调用者返回同一个实例。因此这些对象都不能被修改,这可以极大的提高性能。可写的缓存将通过序列化来返回一个缓存对象的拷贝。这会比较慢,但是比较安全。所以默认值是false

可用的回收算法如下:

  • LRU–最近最少使用:移出最近最长时间内都没有被使用的对象。
  • FIFO–先进先出:移除最先进入缓存的对象。
  • SOFT–软引用: 基于垃圾回收机制和软引用规则来移除对象(空间内存不足时才进行回收)。
  • WEAK–弱引用:基于垃圾回收机制和弱引用规则(垃圾回收器扫描到时即进行回收)。
  • 默认使用LRU

使用自定义缓存
除了上面已经定义好的缓存方式,能够通过您自己的缓存实现来完全重写缓存行为,或者通过创建第三方缓存解决方案的适配器。
<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会自动进行转换。
需要牢记的是一个缓存配置和缓存实例都绑定到一个SQL Map 文件命名空间。因此,所有的这个相同命名空间的语句也都和这个缓存绑定。语句可以修改如何与这个缓存相匹配,或者使用两个简单的属性来完全排除它们自己。默认情况下,语句像下面这样来配置:

<select ... flushCache=”false” useCache=”true”/>
<insert ... flushCache=”true”/>
<update ... flushCache=”true”/>
<delete ... flushCache=”true”/> 

因为有默认值,所以不需要使用这种方式明确地配置这些语句。如果想改变默认的动作,只需要设置flushCacheuseCache属性即可。举个例子来说,在许多的场合下可能排除缓存中某些特定的select语句。或者想用select语句清空缓存。同样的,也可能有一些update 语句在执行的时候不需要清空缓存。

1.1.7 cache-ref元素

在某一个命名空间里使用或者刷新缓存。但有可能您想要在不同的命名空间里共享同一个缓存配置或者实例。在这种情况下,您就可以使用cache-ref 元素来引用另外一个缓存。

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

1.2 延迟加载

1.2.1 什么是延迟加载

延迟加载又叫懒加载,也叫按需加载。也就是说先加载主信息,在需要的时候,再去加载从信息。
mybatis中,resultMap标签的association标签和collection标签具有延迟加载的功能

1.2.2 具体使用

association或者collection中使用嵌入查询方式
Nested Select : 通过执行另一个返回预期复杂类型映射SQL语句(即引用外部定义好的SQL语句块)

<resultMap id="lazyLoadingResultMap" type="com.sxt.model.OrderQuery04">
 
    <!--
        select:指定关联查询的查询statement (即查询用户的statement的id) 
然后将查询结果封装到property属性指定的变量中
        column:clolum指定列的值作为com.test.mapper.UserMapper.findUserById()的查询参数
    -->
    <association property="user" select="com.test.mapper.UserMapper.findUserById" column="user_id"/>
</resultMap>

1.2.3 设置延迟加载

mybatis-config.xml中,配置settings标签

<!-- 加载属性文件 -->
<properties resource="jdbc.properties"/>
<settings>
    <!-- 延迟加载 默认false -->
    <setting name="lazyLoadingEnabled" value="true" />
    <!-- 积极加载 默认true -->
    <setting name="aggressiveLazyLoading" value="false" />
</settings>

点击了解更多关于mybatis配置文件资料

2 类型映射

Mybatis中javaType和jdbcType对应关系

JDBC TypeJava Type
CHARString
VARCHARString
LONGVARCHARString
NUMERICjava.math.BigDecimal
DECIMALjava.math.BigDecimal
BITboolean
BOOLEANboolean
TINYINTbyte
SMALLINTshort
INTEGERint
BIGINTlong
REALfloat
FLOATdouble
DOUBLEdouble
BINARYbyte[]
VARBINARYbyte[]
LONGVARBINARYbyte[]
DATEjava.sql.Date
TIMEjava.sql.Time
TIMESTAMPjava.sql.Timestamp
CLOBClob
BLOBBlob
ARRAYArray
DISTINCTmapping of underlying type
STRUCTStruct
REFRef
DATALINKjava.net.URL[color=red][/color]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值