设为首页 收藏本站
查看: 995|回复: 0

[经验分享] MyBatis Mapper XML 文件 02

[复制链接]

尚未签到

发表于 2016-11-26 02:15:23 | 显示全部楼层 |阅读模式
高级结果映射


下面我们来看看官方文档上提供的
Demo
,一个复杂的查询语句


<select id="selectBlogDetails" parameterType="int" resultMap="detailedBlogResultMap">
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,
A.favourite_section as author_favourite_section,
P.id as post_id,
P.blog_id as post_blog_id,
P.author_id as post_author_id,
P.created_on as post_created_on,
P.section as post_section,
P.subject as post_subject,
P.draft as draft,
P.body as post_body,
C.id as comment_id,
C.post_id as comment_post_id,
C.name as comment_name,
C.comment as comment_text,
T.id as tag_id,
T.name as tag_name
from Blog B
left outer join Author A on B.author_id = A.id
left outer join Post P on B.id = P.blog_id
left outer join Comment C on P.id = C.post_id
left outer join Post_Tag PT on PT.post_id = P.id
left outer join Tag T on PT.tag_id = T.id
where B.id = #{id}
</select>

  Ok,看看对于上面复杂的语句,对应的ResultMap为:

<resultMap id="detailedBlogResultMap" type="Blog">
<constructor>
<idArg column="blog_id" javaType="int"/>
</constructor>
<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"/>
<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" javaType="Author"/>
<collection property="comments" ofType=" Comment">
<id property="id" column="comment_id"/>
</collection>
<collection property="tags" ofType=" Tag" >
<id property="id" column="tag_id"/>
</collection>
<discriminator javaType="int" column="draft">
<case value="1" resultType="DraftPost"/>
</discriminator>
</collection>
</resultMap>

 事情来了,上面
resultMap
有很多节点,下面是
resultMap
元素的概念视图

resultMap



·        



constructor
 -
类在实例化时
,
用来注入结果到构造方法中

·        



idArg
 - ID
参数
;
标记结果作为
ID
可以帮助提高整体效能

·        



arg
 -
注入到构造方法的一个普通结果

·        



id
 –
一个
ID
结果
;
标记结果作为
ID
可以帮助提高整体效能

·        



result
 –
注入到字段或
JavaBean

属性的普通结果

·        



association
 –
一个复杂的类型关联
;
许多结果将包成这种类型

·        



嵌入结果映射

结果映射自身的关联
,
或者参考一个

·        



collection
 –
复杂类型的集

·        



嵌入结果映射

结果映射自身的集
,
或者参考一个

·        



discriminator
 –
使用结果值来决定使用哪个结果映射

·        



case
 –
基于某些值的结果映射

·        



嵌入结果映射

这种情形结果也映射它本身
,
因此可以包含很多相

同的元素
,
或者它可以参照一个外部的结果映射。

 

下面我们来详细说明每个元素

id
& result





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

这些是结果映射最基本内容。
id

result
都映射一个单独列的值到简单数据类型
(
字符

,
整型
,
双精度浮点数
,
日期等
)
的单独属性或字段。

这两者之间的唯一不同是
id
表示的结果将是当比较对象实例时用到的标识属性。这帮助来改进整体表现
,
特别是缓存和嵌入结果映射
(
也就是联合映射
)


 


Id and Result Attributes



属性





描述





property



映射到列结果的字段或属性。如果匹配的是存在的
,
和给定名称相同



JavaBeans

的属性
,
那么就

会使用。否则

MyBatis

将会寻找给定名称

property

的字段。这两种情形你可以使用通常点式的复

杂属性导航。比如
,


可以这样映射一些东西
:
“username” ,

或者映射到一些复杂的东西
:

 
“address.street.number”





column



从数据库中得到的列名
,
或者是列名的重命名标签。这也是通常和会

传递给

resultSet.getString


(columnName)
方法参数中相同的字符串。



javaType



一个
Java
类的完全限定名
,
或一个类型别名
(
参加上面内建类型别名

的列表
)
。如果你映射到一个
JavaBean,MyBatis
通常可以断定类型。

然而
,
如果你映射到的是
HashMap,
那么你应该明确地指定
javaType
来保证所需的行为。



jdbcType



在这个表格之后的所支持的
JDBC
类型列表中的类型。
JDBC
类型是仅

仅需要对插入
,
更新和删

除操作可能为空的列进行处理。这是
JDBC
jdbcType

的需要
,
而不是
MyBatis
的。如果你直接使


JDBC
编程
,
你需要指定

这个类型
-
但仅仅对可能为空的值。



typeHandler



我们在前面讨论过默认的类型处理器。使用这个属性
,
你可以覆盖默

认的类型处理器。这个属性值

是类的完全限定名或者是一个类型处理

器的实现
,
或者是类型别名。



构造方法:



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

  这里需要注意id和username的顺序问题,不能颠倒。

public class User {
//...
public User(int id, String username) {
//...
}
//...
}

 关联



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

关联元素处理“有一个”类型的关系。比如
,
在我们的示例中
,
一个博客有一个用户。
关联映射就工作于这种结果之上。你指定了目标属性
,
来获取值的列
,
属性的
java
类型
(

多情况下
MyBatis
可以自己算出来
) ,
如果需要的话还有
jdbc
类型
,
如果你想覆盖或获取的
结果值还需要类型控制器。

关联中不同的是你需要告诉
MyBatis
如何加载关联。
MyBatis
在这方面会有两种不同的方式:

嵌套查询:通过执行另外一个
SQL
映射语句来返回预期的复杂类型。

嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集。首先
,
然让我们来查看这个元素的属性。所有的你都会看到
,
它和普通的只由
select

resultMap
属性的结果映射不同。

 

关联的嵌套查询


 


属性





描述





column



来自数据库的类名
,
或重命名的列标签。这和通常传递给

resultSet.getString(columnName)

方法的字

符串是相同的。
column




:













,






















 
column= ” {prop1=col1,prop2=col2} ”

这种语法来传递给嵌套查询语

句。这会引起
prop1

prop2

 

以参数对象形式来设置给目标嵌套查询语句。



select



另外一个映射语句的
ID,
可以加载这个属性映射需要的复杂类型。获取的

在列属性中指定的列的值将被

传递给目标
select

语句作为参数。表格后面

有一个详细的示例。
select




:












 


,






















column= ” {prop1=col1,prop2=col2} ”

这种语法来传递

给嵌套查询语

句。这会引起
prop1

prop2
以参数对象形式来设置给目标嵌套查询语句。



 

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

我们有两个查询语句
:
一个来加载博客
,
另外一个来加载作者
,
而且博客的结果映射描
述了“
selectAuthor
”语句应该被用来加载它的
author
属性。

其他所有的属性将会被自动加载
,
假设它们的列和属性名相匹配。

这种方式很简单
,
但是对于大型数据集合和列表将不会表现很好。问题就是我们熟知的

N+1
查询问题”。概括地讲
,N+1
查询问题可以是这样引起的
:

 

你执行了一个单独的
SQL
语句来获取结果列表
(
就是“
+1

)


对返回的每条记录
,
你执行了一个查询语句来为每个加载细节
(
就是“
N

)


这个问题会导致成百上千的
SQL
语句被执行。这通常不是期望的。

MyBatis
能延迟加载这样的查询就是一个好处
,
因此你可以分散这些语句同时运行的消耗。然而
,
如果你加载一个列表
,
之后迅速迭代来访问嵌套的数据
,
你会调用所有的延迟加载
,
这样的行为可能是很糟糕的。

所以还有另外一种方法。

 
关联的嵌套结果


 


属性





描述





resultMap



这是结果映射的
ID,
可以映射关联的嵌套结果到一个合适的对象图中。这

是一种替代方法来调

用另外一个查询语句。这允许你联合多个表来合成到

resultMap

一个单独的结果集。这样的结

果集可能包含重复
,
数据的重复组需要被分


,
合理映射到一个嵌套的对象图。为了使它变得容


,MyBatis

让你





结果映射
,
来处理嵌套结果。一个例子会很容易来仿照
,
这个表格后

面也

有一个示例。



columnPrefix



当加入多个表
,
你将不得不使用列别名
,
以避免重复列名在
ResultSet


指定
columnPrefix
允许

您映射到一个外部等栏目
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="id" column="blog_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>

  简单来说就是需要关联哪些字段,才去查哪些字段。上面的还可以改为:

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

上面的关系都是一对一的情况,下面我们来看一下一对多个的情况

 
集合的嵌套查询


首先
,
让我们看看使用嵌套查询来为博客加载文章


<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="Blog">
SELECT * FROM POST WHERE BLOG_ID = #{id}
</select>

这里你应该注意很多东西
,
但大部分代码和上面的关联元素是非常相似的。首先
,
你应
该注意我们使用的是集合元素。然后要注意那个新的“
property
”属性。这个属性是指
JavaBean
(博客类中文章属性集合)中属性类型。
ofType
指的是具体集合中每个元素的类型。所以你可以读出下面这个映射
:


<collection property="posts" javaType="ArrayList" column="blog_id" ofType="Post" select="selectPostsForBlog"/>
读作
:
“在
Post
类型的
ArrayList
中的
posts
的集合。”

javaType
属性是不需要的
,
因为
MyBatis
在很多情况下会为你算出来。所以你可以缩短写法
:


<collection property="posts" column="blog_id" ofType="Post" select="selectPostsForBlog"/>
 
<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>

 
<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" columnPrefix="post_"/>
</resultMap>
<resultMap id="blogPostResult" type="Post">
<id property="id" column="id"/>
<result property="subject" column="subject"/>
<result property="body" column="body"/>
</resultMap>

如果大家学了
Hibernate
,再来看看这个,应该来说还是相对好理解点的。

 

鉴别器



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

有时一个单独的数据库查询也许返回很多不同
(
但是希望有些关联
)
数据类型的结果集。
鉴别器元素就是被设计来处理这个情况的
,
还有包括类的继承层次结构。鉴别器非常容易理

,
因为它的表现很像
Java
语言中的
switch
语句。

定义鉴别器指定了
column

javaType
属性。
列是
MyBatis
查找比较值的地方。
JavaType
是需要被用来保证等价测试的合适类型
(
尽管字符串在很多情形下都会有用
)
。比如
:


<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
包含一个非常强大的查询缓存特性
,
它可以非常方便地配置和定制。
MyBatis 3
中的缓存实现的很多改进都已经实现了
,
使得它更加强大而且易于配置。

默认情况下是没有开启缓存的
,
除了局部的
session
缓存
,
可以增强变现而且处理循环
依赖也是必须的。要开启二级缓存
,
你需要在你的
SQL
映射文件中添加一行
:


<cache/>
字面上看就是这样。这个简单语句的效果如下
:

映射语句文件中的所有
select
语句将会被缓存。

映射语句文件中的所有

insert,update

delete
语句会刷新缓存。

缓存会使用
Least Recently
Used(LRU,
最近最少使用的
)
算法来收回。

根据时间表
(
比如
no Flush
Interval,
没有刷新间隔
),
缓存不会以任何时间顺序
来刷新。

缓存会存储列表集合或对象
(
无论查询方法返回什么
)

1024
个引用。

缓存会被视为是
read/write(
可读
/
可写
)
的缓存
,
意味着对象检索不是共享的
,

且可以安全地被调用者修改
,
而不干扰其他调用者或线程所做的潜在修改。

所有的这些属性都可以通过缓存元素的属性来修改。比如
:


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

这个更高级的配置创建了一个
FIFO
缓存
,
并每隔
60
秒刷新
,
存数结果对象或列表的
512
个引用
,
而且返回的对象被认为是只读的
,
因此在不同线程中的调用者之间修改它们会
导致冲突。

可用的收回策略有(默认的是
LRU

:

LRU

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

FIFO

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

SOFT

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

WEAK

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

 

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
会进行转换。

记得缓存配置和缓存实例是绑定在
SQL
映射文件的命名空间是很重要的。因此
,
所有
在相同命名空间的语句正如绑定的缓存一样。
语句可以修改和缓存交互的方式
,
或在语句的
语句的基础上使用两种简单的属性来完全排除它们。默认情况下
,
语句可以这样来配置
:


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

因为那些是默认的
,
你明显不能明确地以这种方式来配置一条语句。相反
,
如果你想改
变默认的行为
,
只能设置
flushCache

useCache
属性。比如
,
在一些情况下你也许想排除
从缓存中查询特定语句结果
,
或者你也许想要一个查询语句来刷新缓存。相似地
,
你也许有
一些更新语句依靠执行而不需要刷新缓存。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-305464-1-1.html 上篇帖子: Mybatis物理分页插件(目前mybatis下最好的物理分页) 下篇帖子: mybatis学习笔记(一) 入门demo
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表