**
示例业务关系模型
**
这是MyBatis文档中提到的一个经典例子~~
每个用户的博客 Blog,其下会发表有 N 篇博文 Post;
对于每一篇博文 Post,会对应 N 个评论 Comment,以及多种博文标签 Post_Tag;
建立关系数据表
数据表的列命名采用下划线分隔形式,如 post_id建立数据表对应的实体类
建议使用 mybatis-generator 为每个数据表生成其实体类,
我们暂且称这种对应于数据表的类为 “元对象”
假定应用中要获取这样的复合信息,即要查询某个博客的作者、发布的所有博文及其评论信息
目前没有一个对象结构反映这样的需求,所以,
- 定义业务需要的复合对象

即我们的数据聚合查询接口,需要返回这样的一个对象(某博客)
Author 对应的是 domain下的Author实体类(元对象),而 Post 则是另外一个 复合对象:
其定义聚合了 作者,评论,标签三种 元对象,
至此,对象结构已经定义好:
Blog,Post 都是复合对象,由应用元对象组合而成;宜区分采用单独的包存放其定义:

这一步中,实际上是定义了我们应用中的数据查询返回的结果对象结构
- 定义Mapper查询接口
Blog selectBlogDetails(@Param(“id”) Integer id);
- Mapper XML中定义SQL查询

resultMap detailedBlogResultMap是最顶层的映射
选取查询的基准表,此处为 Blog,连接所需的其他数据表(需要的字段出处)写出查询语句
其中,采用如下的规范:
1)为查询的数据表定义别名;
2)为结果集定义字段别名,以数据表名前缀 + 字段名,如 blog_id,对应的是blog表的id字段;
3)来自同一数据表的字段在查询语句中放在一起;
这样规范的好处见后续
至此,查询语句可以从数据库中取得含目标数据集的临时表,简单表示为如下结构:
- ORM 映射
现在,就是要利用强大的 resultMap 建立 5中的数据表 与 对象模型之间的映射关系
建立映射,
1)采取从高到低的定义顺序
2)采用反映对象模型结构的 resultMap 来定义
3)建立 resultMap 树形定义
顶级 detailedBlogResultMap 的定义如下:
property:对象模型中的属性名
column:对应查询结果数据中的字段名
association:一对一的关联
collection:一对多的列表结构 对比一下复合结构 Blog:

可以发现,resultMap的定义与Blog的结构是一一对应的关系
对于 authorResult 这个resultMap,其对应单个数据表(Author)的映射关系,我们姑且将这种resultMap称为 元映射:
元映射 可以看作是单表内原始数据库字段和其实体类 domain/Author 直接的映射定义
columnPrefix=“author_”
此处,是在原始数据表字段名上加上前缀 author_,作为查询结果集的字段名;
这正是我们在定义查询出的字段别名时,在字段名前面添加 表名_ 前缀的规范用处
接下来,对于Post,也是一个复合结构,其定义:
其对应的 resultMap 为postResultMap,是 复合映射:
对比对象结构和映射定义,两者也是一一对应的关系
至此,comments,tags 属性都已是 元映射,postResultMap不再包含 复合结构了。
因此,映射的体系结构如图,总体来看是一个树形的层次结构,
这样规范化定义:
1)结构性比较清晰自然;
2)提高复用性,减少出错;
总结:
1)元映射,meta resultmap,
对应的是一个数据表的映射关系;是映射树的最底层(基础层),对应的对象为数据表实体类
2)顶层映射, root resultmap,
对应的是查询结果映射,其对应的对象结构是复合的类定义
3)顶层映射下的层次结构中的复合映射,composite resultmap,
每一个也一一对应一个复合的类定义
附:XML
<?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="com.example.bootdemo.mapper.BlogMapper">
<!--
查询结果列名字命名:
1)加表名最为前缀;
2)指定columnPrefix为表名前缀
-->
<select id="selectBlogDetails" 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 post_draft,
P.body as post_body,
C.id as comment_id,
C.post_id as comment_post_id,
C.name as comment_name,
C.text 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>
<!--
最终复杂结果映射定义
复杂映射:对应对象的结构,层层分解
association;collection均分解为元映射定义
-->
<resultMap id="detailedBlogResultMap" type="com.example.bootdemo.vo.Blog">
<id property="id" column="blog_id" />
<result property="title" column="blog_title"/>
<association property="author" resultMap="authorResult" columnPrefix="author_" />
<collection property="posts" resultMap="postResultMap" />
</resultMap>
<resultMap id="postResultMap" type="com.example.bootdemo.vo.Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<association property="author" resultMap="authorResult" columnPrefix="author_" />
<collection property="comments" resultMap="commentMap" columnPrefix="comment_" />
<collection property="tags" resultMap="tagMap" columnPrefix="tag_" />
</resultMap>
<!--
元映射定义
-->
<resultMap id="commentMap" type="Comment">
<id property="id" column="id"/>
<result property="postId" column="post_id" />
<result property="name" column="name" />
<result property="text" column="text" />
</resultMap>
<resultMap id="tagMap" type="Tag">
<id property="id" column="id"/>
<result property="name" column="name" />
</resultMap>
<resultMap id="authorResult" type="Author">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<result property="email" column="email"/>
<result property="bio" column="bio"/>
<result property="favouriteSection" column="favourite_section"/>
</resultMap>
</mapper>