ResultMap元素是MyBaits中最重要最强大的元素。它可以让你从90%的JDBCResultSets数据提取代码中解放出来,并在一些情形下允许你做一些JDBC不支持的事情。实际上,在对复杂语句进行联合映射的时候,它很可能可以代替数千行的同等功能的代码。ResultMap的设计思想是——简单的语句不需要明确的结果映射,而复杂一点的语句只需要描述它们的关系就行了。
以常用的javaBean对象为例:
首先,我们基于javaBean规范创建一个类:
public class User {
private int id;
private String username;
private String hashedPassword;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getHashedPassword() {
return hashedPassword;
}
public void setHashedPassword(String hashedPassword) {
this.hashedPassword = hashedPassword;
}
}
该类中的属性名(id,username,hashedPassword)将会对应到select语句中的列名
这样的一个javaBean可以被映射到ResultSet
<select id="selectUsers" resultType="User">
select id, username, hashedPassword
from some_table
where id = #{id}
</select>
注意,resultType需要完全限定名称,例如:com.xxxx.xxxx.User,这里直接写User是因为在全局配置文件中声明了类型别名
在这种情况下,Mybaits会自动创建一个ResultMap,再基于属性名来映射列到JavaBean的属性上,注意一定要有相应属性的get、set方法。如果列名和属性名没有精确匹配,可以在SELECT语句中对列使用别名来匹配标签。比如:
<select id="selectUsers" resultType="User">
select
user_id as "id",
user_name as "userName",
hashed_password as "hashedPassword"
from some_table
where id = #{id}
</select>
以上是隐式的使用方法,适用于一些简单的映射,可是如果我们的查询结果关联到多个表,此时是隐式的ResultMap所无法解决的,需要使用到显示的用法。
例如需要执行下面这条SQL:
<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 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如下所示:
<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结果,标记作为ID的结果可以帮助提高整体性能-->
<id property="id" column="author_id"/>
<!--result注入字段或JavaBean属性的普通结果-->
<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>
<!--使用结果值来决定使用哪个resultMap-->
<discriminator javaType="int" column="draft">
<!--一个case也是一个映射它本身的结果,因此可以包含很多相同的元素,或者它可以参照另一个resultMap-->
<case value="1" resultType="DraftPost"/>
</discriminator>
</collection>
</resultMap>
注意ResultMap中一对一的关系和一对多的关系写法,对应的是javaBean中属性为另一个bean和另一个bean的集合。
极为方便的是集合中所对应的bean仍可以直接表示一对多的关系(比如此例中博客对多篇博文,而每篇博文又对多条评论),这大大地减少了我们写代码的工作量,极为实用!