高级结果映射

MyBatis 创建的一个想法:数据库不用永远是你想要的或需要它们是什么样的。而我们最喜欢的数据库最好是第三范式或 BCNF 范式,但它们有时不是。如果可能有一个单独的

数据库映射,所有应用程序都可以使用它,这是非常好的,但有时也不是。结果映射就是MyBatis 提供处理这个问题的答案。

比如,我们如何映射下面这个语句?

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

你可能想把它映射到一个智能的对象模型,包含一个作者写的博客,有很多的博文,每篇博文有零条或多条的评论和标签。下面是一个完整的复杂结果映射例子(假设作者,博

客,博文,评论和标签都是类型的别名)。

<!-- 非常复杂的结果映射 -->

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

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 表示的结果将是当比较对象实例时用到的标识属性。这帮助来改进整体表现,特别是缓存和嵌入结果映射(也就是联合映射)。

每个都有一些属性:



支持的 JDBC类型 :

为了未来的参考,MyBatis 通过包含的 jdbcType 枚举型,支持下面的 JDBC 类型。



构造方法:

<constructor>

<idArg column="id"  javaType="int"/>

<arg column=”username” javaType=”String”/>

</constructor>


对于大多数数据传输对象(Data Transfer Object,DTO)类型,属性可以起作用,而且像绝大多数的领域模型,指令也许是你想使用一成不变的类的地方。通常包含引用或查询

数据的表很少或基本不变的话对一成不变的类来说是合适的。构造方法注入允许你在初始化时为类设置属性的值,而不用暴露出公有方法。MyBatis 也支持私有属性和私有

 JavaBeans 属性来达到这个目的,但是一些人更青睐构造方法注入。Constructor(构造方法)元素支持这个。

看看下面这个构造方法:

public class User {

//…

public User(int id, String username) {

//…

}

//…

}

为了向这个构造方法中注入结果,MyBatis 需要通过它的参数的类型来标识构造方法。Java 没有自查(或反射)参数名的方法。所以当创建一个构造方法元素时,保证参数是按

顺序排列的,而且数据类型也是确定的。

<constructor>

<idArg column="id" javaType="int"/>

<arg column=”username” javaType=”String”/>

</constructor>

剩余的属性和规则和固定的 id 和 result 元素是相同的。



关联:

<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 属性的结果映射不同。



关联的嵌套查询:



示例:

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

加载,这样的行为可能是很糟糕的。所以还有另外一种方法。


关联的嵌套:


在上面你已经看到了一个非常复杂的嵌套关联的示例。下面这个是一个非常简单的示例来说明它如何工作。代替了执行一个分离的语句,我们联合博客表和作者表在一起,就

像:

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

在上面的示例中你可以看到博客的作者关联代表着“authorResult” 结果映射来加载作者实例。

非常重要:在嵌套结果映射中 id 元素扮演了非常重要的角色。应该通常指定一个或多个属性,它们可以用来唯一标识结果。实际上就是如果你不使用它(id 元素),但是会产

生一个严重的性能问题,不过 MyBatis 仍然可以正常工作。选择的属性越少越好,它们可以唯一地标识结果。主键就是一个显而易见的选择(即便是联合主键)。


现在,上面的示例用了外部的 结果映射 元素来映射关联。这使得 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>


集合:

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

集合 元素的作用几乎和关联是相同的。实际上,它们也很相似,文档的异同是多余的。所以我们更多关注于它们的不同。

我们来继续上面的示例,一个博客只有一个作者。但是博客有很多文章。在博客类中,这可以由下面这样的写法来表示:

private List<Post> posts;

要映射嵌套结果集合到 List 中,我们使用 集合 元素。就像 关联 元素一样,我们可以从连接中使用嵌套查询,或者嵌套结果。

集合的嵌套查询:

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

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

这里你应该注意很多东西,但大部分代码和上面的 关联 元素是非常相似的。首先,你应该注意我们使用的是 集合 元素。然后要注意那个新的“ofType”属性。这个属性用来区分

JavaBean(或字段)属性类型和集合包含的类型来说是很重要的。所以你可以读出下面这个映射:

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


集合的嵌套结果:

至此,你可以猜测集合的嵌套结果是如何来工作的,因为它和关联完全相同,除了它应用了一个“ofType”属性

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




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值