使用mybatis,有两个属性标签<resultType>
、<resultMap>
可以提供结果映射。
虽然resultType
属性在大部分情况下都够用,但是在一些特殊情况下无能为力,比如属性名和列名不一致,为一些连接的复杂语句编写映射代码。
遇到这些情况,我们要使用<resultMap>
标签,一份 <resultMap>
能够代替实现同等功能的数千行代码。
resultMap 元素是 MyBatis 中最重要最强大的元素。
resultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
属性名和列名不一致
这是开发过程中常见的情境,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;
}
}
<select id="selectUsers" resultType="User">
select
user_id,user_name,hashed_password
from some_table
where id = #{id}
</select>
为了解决上述问题,我们只需要在 <resultMap>
中做一下简单的配置,然后在引用它的语句中设置 <resultMap>
属性就行了。
<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="username" column="user_name"/>
<result property="password" column="hashed_password"/>
</resultMap>
<select id="selectUsers" resultMap="userResultMap">
select user_id, user_name, hashed_password
from some_table
where id = #{id}
</select>
注意:这里去掉了
<reslutType>
属性,用<resultMap>
代替,二者只能选择其中的一个。
高级结果映射
MyBatis 创建时的一个思想是:数据库不可能永远是你所想或所需的那个样子。
我们希望每个数据库都具备良好的第三范式或 BCNF 范式,可惜它们并不都是那样。
如果能有一种数据库映射模式,完美适配所有的应用程序,那就太好了,可惜没有。 而 ResultMap 就是 MyBatis 对这个问题的答案。
一对一映射
比如,我们如何映射下面这个语句?
<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
from Blog B
left outer join Author A on B.author_id = A.id
where B.id = #{id}
</select>
这是典型的一对一的关联关系情况,我们通过<association>
配置就可以解决这个问题。
<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>
</resultMap>
映射在 javaBean 中的表示:
private Author author;
接下来,我们一步一步来看这些元素的意义。
reslutMap 的属性
id:当前命名空间中的一个唯一标识,用于标识一个结果映射。
type:类的完全限定名, 或者一个类型别名。
id和result
id 和 result 元素都将一个列的值映射到一个简单数据类型(String, int, double, Date 等)的属性或字段。
这两者之间的唯一不同是,id 元素对应的属性会被标记为对象的标识符,在比较对象实例时使用。 这样可以提高整体的性能,尤其是进行缓存和嵌套结果映射(也就是连接映射)的时候。
两者的一些属性:
property:映射到列结果的字段或属性。
column:数据库中的列名,或者是列的别名。
javaType:一个 Java 类的全限定名,或一个类型别名。通常不会配置,mybatis 能够根据参数信息自动识别,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。
jdbcType:JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。
多对多映射
首先来看对应的 SQL 语句:
<select id="selectBlog" 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>
javaBean 中我们这样表示集合:
private List<Post> posts;
大部分都和我们上面学习过的关联元素非常相似,这里只是新增了一个 ofType
属性。
这个属性非常重要,它用来将 JavaBean(或字段)属性的类型和集合存储的类型区分开来。 所以你可以按照下面这样来阅读映射:
<collection property="posts" javaType="ArrayList" ofType="Post"/>
读作: “posts 是一个存储 Post 的 ArrayList 集合”。
在一般情况下,MyBatis 可以推断 javaType 属性,因此并不需要填写。所以很多时候你可以简略成:
<collection property="posts" ofType="Post"/>
高级关联和集合映射是一个深度话题。文章的介绍只能到此为止。配合少许的实践,你会很快了解全部的用法。