resultMap 元素
resultMap元素是MyBatis中最重要最强大的元素。与使用JDBC从结果集获取数据相比,它可
以省掉90%的代码,也可以允许您做一些JDBC不支持的事。事实上,要写一个类似于连结映射
(join mapping)这样复杂的交互代码,可能需要上千行的代码。设计ResultMaps 的目的,就是
只使用简单的配置语句而不需要详细地处理结果集映射,对更复杂的语句除了使用一些必须的语
句描述以外,就不需要其它的处理了。
您可能已经看到过这样简单映射语句,它并没有使用 resultMap,例如:
<select id=”selectUsers” parameterType=”int” resultType=”hashmap”>
select id, username, hashedPassword
from some_table
where id = #{id}
</sql>
像上面的语句,所有结果集将会自动地映射到以列表为key 的HasMap(由resultType指定)
中。虽然这对许多场合下有用,但是HashMap 却不是非常好的域模型。更多的情况是使用
JavaBeans或者POJOs作为域模型。MyBatis支持这两种域模型。考虑下面的JavaBean:
package com.someapp.model;
public class User {
private int id;
private String username;
private String hashedPassword;
public int getId() {
MyBatis 3 - User Guide
31
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;
}
}
基于 JavaBeans 规范,上面的类有 3 个属性: id、username,和 hashedPassword 。这 3 个
属性对应 select 语句的列名。
这样的JavaBean 可以像HashMap 一样简单地映射到 ResultSet 结果集。
<select id=”selectUsers” parameterType=”int”
resultType=”com.someapp.model.User”>
select id, username, hashedPassword
from some_table
where id = #{id}
</sql>
别忘了别名是您的朋友,使用别名您不用输入那长长的类路径。例如:
<!-- In Config XML file -->
<typeAlias type=”com.someapp.model.User” alias=”User”/>
<!-- In SQL Mapping XML file -->
<select id=”selectUsers” parameterType=”int”
resultType=”User”>
select id, username, hashedPassword
from some_table
where id = #{id}
</sql>
这种情况下,MyBatis 在后台自动生成 ResultMap,将列名映射到 JavaBean 的相应属性。如
果列名与属性名不匹配,可以使用 select 语法(标准的 SQL 特性)中的将列名取一个别名的方式
来进行匹配。例如
<select id=”selectUsers” parameterType=”int” resultType=”User”>
MyBatis 3 - User Guide
32
select
user_id
as “id”,
user_name
as “userName”,
hashed_password
as “hashedPassword”
from some_table
where id = #{id}
</sql>
ResultMaps 的知识您可能已经学到了许多,但还有一个您从没见到过。为了举例,让我们看
看最后一个例子,作为另一种解决列名不匹配的方法。
<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="username" column="username"/>
<result property="password" column="password"/>
</resultMap>
这个语句将会被 resultMap 属性引用(注意,我们没有使用 resultType)。如:
<select id=”selectUsers” parameterType=”int” resultMap=”userResultMap”>
select user_id, user_name, hashed_password
from some_table
where id = #{id}
</sql>
一切就是这么简单!
高级结果映射
MyBatis的创建基于这样一个思想:数据库并不是您想怎样就怎样的。虽然我们希望所有的数
据库遵守第三范式或BCNF(修正的第三范式),但它们不是。如果有一个数据库能够完美映射到
所有应用数据模型,也将是非常棒的,但也没有。结果集映射就是MyBatis为解决这些问题而提供
的解决方案。例如,我们如何映射下面这条语句?
<!-- Very Complex Statement -->
<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,
MyBatis 3 - User Guide
33
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>
您可能想要把它映射到一个智能的对象模型,包括由一个作者写的一个博客,有许多文章
(Post,帖子),每个文章由0个或者多个评论和标签。下面是一个复杂ResultMap 的完整例子
(假定作者、博客、文章、评论和标签都是别名)。仔细看看这个例子,但是不用太担心,我们
会一步步地来分析,一眼看上去可能让人沮丧,但是实际上非常简单的
<!-- Very Complex Result Map -->
<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 的元素的子元素比较多,讨论起来比较宽泛。下面我们从概念上概览一下这
MyBatis 3 - User Guide
34
个resultMap的元素。
resultMap
•
constructor – 实例化的时候通过构造器将结果集注入到类中
o
idArg – ID 参数; 将结果集标记为ID,以方便全局调用
o
arg – 注入构造器的结果集
•
id –结果集ID,将结果集标记为 ID,以方便全局调用
•
result – 注入一个字段或者 javabean 属性的结果
•
association – 复杂类型联合; 许多查询结果合成这个类型
o
嵌套结果映射 – associations 能引用自身, 或者从其它地方引用
•
collection – 复杂类型集合
o
嵌套结果映射– collections 能引用自身, 或者从其它地方引用
•
discriminator –使用一个结果值以决定使用哪个 resultMap
o
case – 基于不同值的结果映射
�
嵌套结果映射 –case 也能引用它自身, 所以也能包含这些同样的元
素。它也可以从外部引用 resultMap
� 最佳实践:
逐步地生成resultMap,单元测试对此非常有帮助。如果您尝试一下子就
生成像上面这样巨大的resultMap,可能会出错,并且工作起来非常吃力。从简单地开
始,再一步步地扩展,并且进行单元测试。使用框架开发有一个缺点,它们有时像是一个
黑盒子。为了确保达到您所预想的行为,最好的方式就是进行单元测试。这对提交 bugs
也非常有用。
下一节,我们一步步地查看这些细节。
id, result 元素
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
这是最基本的结果集映射。id 和 result 将列映射到属性或简单的数据类型字段 (String,
int, double, Date 等)。
这两者唯一不同的是,在比较对象实例时 id 作为结果集的标识属性。这有助于提高总体性
能,特别是应用缓存和嵌套结果映射的时候。
id、result 属性如下:
Attribute
Description
property
映射数据库列的字段或属性。如果JavaBean 的属性与给定的名称匹配,就会使用
匹配的名字。否则,MyBatis 将搜索给定名称的字段。两种情况下您都可以使用
逗点的属性形式。比如,您可以映射到“username”,也可以映射到
“address.street.number”。
column
数据库的列名或者列标签别名。与传递给 resultSet.getString(columnName)的
参数名称相同。
javaType
完整 java 类名或别名 (参考上面的内置别名列表)。如果映射到一个 JavaBean,
那 MyBatis 通常会自行检测到。然而,如果映射到一个 HashMap,那您应该明确
MyBatis 3 - User Guide
35
支持的 JDBC 类型
MyBatis 支持如下的 JDBC 类型:
Constructor 元素
<constructor>
<idArg column="id" javaType="int"/>
<arg column=”username” javaType=”String”/>
</constructor>
当属性与DTO,或者与您自己的域模型一起工作的时候,许多场合要用到不变类。通常,包含
引用,或者查找的数据很少或者数据不会改变的的表,适合映射到不变类中。构造器注入允许您
在类实例化后给类设值,这不需要通过public方法。MyBatis 同样也支持private 属性和
JavaBeans 的私有属性达到这一点,但是一些用户可能更喜欢使用构造器注入。构造器元素可以
做到这点。
考虑下面的构造器:
public class User {
//…
public User(int id, String username) {
//…
}
//…
}
为了将结果注入构造器,MyBatis 需要使用它的参数类型来标记构造器。Java 没有办法通过
参数名称来反射获得。因此当创建 constructor 元素,确保参数是按顺序的并且指定了正确的类
型。
<constructor>
<idArg column="id" javaType="int"/>
<arg column=”username” javaType=”String”/>
</constructor>
指定 javaType 来确保所需行为。
jdbcType
这张表下面支持的 JDBC 类型列表列出的JDBC 类型。这个属性只在 insert,
update 或 delete 的时候针对允许空的列有用。JDBC 需要这项,但 MyBatis 不
需要。如果您直接编写JDBC 代码,在允许为空值的情况下需要指定这个类型。
typeHandler
我们已经在文档中讨论过默认类型处理器。使用这个属性可以重写默认类型处理
器。它的值可以是一个TypeHandler 实现的完整类名,也可以是一个类型别名。
BIT
FLOAT
CHAR
TIMESTAMP
OTHER
UNDEFINED
TINYINT
REAL
VARCHAR
BINARY
BLOB
NVARCHAR
SMALLINT
DOUBLE
LONGVARCHAR
VARBINARY
CLOB
NCHAR
INTEGER
NUMERIC
DATE
LONGVARBINARY
BOOLEAN
NCLOB
BIGINT
DECIMAL
TIME
NULL
CURSOR
MyBatis 3 - User Guide
36
其它的属性与规则与 id、result 元素的一样。
Association 元素
<association property="author" column="blog_author_id" javaType=" Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
</association>
Association 元素处理“has-one”(一对一)这种类型关系。比如在我们的例子中,一个
Blog 有一个Author。联合映射与其它的结果集映射工作方式差不多,指定property、column、
javaType(通常 MyBatis 会自动识别)、jdbcType(如果需要)、typeHandler。
不同的地方是您需要告诉 MyBatis 如何加载一个联合查询。MyBatis 使用两种方式来加载:
•
Nested Select:
通过执行另一个返回预期复杂类型的映射SQL 语句(即引用外部定义好
的 SQL 语句块)。
•
Nested Results:
通过嵌套结果映射(nested result mappings)来处理联接结果集
(joined results)的重复子集。
首先,让我们检查一下元素属性。正如您看到的,它不同于普通只有select 和 resultMap 属
性的结果映射。
Attribute
Description
column
数据库的列名或者列标签别名。与传递给 resultSet.getString(columnName)的
参数名称相同。
javaType
完整 java 类名或别名 (参考上面的内置别名列表)。如果映射到一个 JavaBean,
那 MyBatis 通常会自行检测到。然而,如果映射到一个 HashMap,那您应该明确
指定 javaType 来确保所需行为。
jdbcType
支持的JDBC 类型列表中列出的 JDBC 类型。这个属性只在insert,update 或
delete 的时候针对允许空的列有用。JDBC 需要这项,但 MyBatis 不需要。如果
您直接编写JDBC 代码,在允许为空值的情况下需要指定这个类型。
typeHandler
我们已经在文档中讨论过默认类型处理器。使用这个属性可以重写默认类型处理
器。它的值可以是一个TypeHandler 实现的完整类名,也可以是一个类型别名。
Attribute
Description
property
映射数据库列的字段或属性。如果 JavaBean 的属性与给定的名称匹配,就会使
用匹配的名字。否则,MyBatis 将搜索给定名称的字段。两种情况下您都可以使
用逗点的属性形式。比如,您可以映射到”username”,也可以映射到更复杂点
的”address.street.number”。
column
数据库的列名或者列标签别名。与传递给 resultSet.getString(columnName)的
参数名称相同。
注意: 在处理组合键时, 您可以使用column= “{prop1=col1,prop2=col2}”
这样的语法,设置多个列名传入到嵌套查询语句。这就会把prop1 和 prop2 设置
MyBatis 3 - User Guide
37
联合嵌套选择(Nested Select for Association)
例如:
<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>
我们使用两个 select 语句:一个用来加载 Blog,另一个用来加载 Author。Blog 的
resultMap 描述了使用“selectAuthor”语句来加载author 的属性。
如果列名和属性名称相匹配的话,所有匹配的属性都会自动加载。
到目标嵌套选择语句的参数对象中。
javaType
完整 java 类名或别名 (参考上面的内置别名列表)。如果映射到一个 JavaBean,
那 MyBatis 通常会自行检测到。然而,如果映射到一个 HashMap,那您应该明确
指定 javaType 来确保所需行为。
jdbcType
支持的JDBC 类型列表中列出的 JDBC 类型。这个属性只在insert,update 或
delete 的时候针对允许空的列有用。JDBC 需要这项,但 MyBatis 不需要。如果
您直接编写JDBC 代码,在允许为空值的情况下需要指定这个类型。
typeHandler
我们已经在文档中讨论过默认类型处理器。使用这个属性可以重写默认类型处理
器。它的值可以是一个TypeHandler 实现的完整类名,也可以是一个类型别名。
select
通过这个属性,通过 ID 引用另一个加载复杂类型的映射语句。从指定列属性中返
回的值,将作为参数设置给目标select 语句。表格下方将有一个例子。注意:
在处理组合键时,您可以使用 column=”{prop1=col1,prop2=col2}”这样的语
法,设置多个列名传入到嵌套语句。这就会把 prop1 和 prop2 设置到目标嵌套语
句的参数对象中。
译者注:
上面的例子,首先执行<select id=“selectBlog”>,执行结果存放到
<resultMap id=“blogResult”>结果映射中。“blogResult”是一个Blog 类型,
从<select id=“selectBlog”>查出的数据都会自动赋值给”blogResult”的与列
名匹配的属性,这时 blog_id,title 等就被赋值了。同时“blogResult”还有一
个关联属性"Author",执行嵌套查询select=”selectAuthor”后,Author 对象的
属性 id,username,password,email,bio 也被赋于与数据库匹配的值。
Blog
{
MyBatis 3 - User Guide
38
虽然这个方法简单,但是对于大数据集或列表查询,就不尽如人意了。这个问题被称为“N+1
选择问题”(N+1 Selects Problem)。概括地说,N+1选择问题是这样产生的:
•
您执行单条SQL 语句去获取一个列表的记录 ( “+1”)。
•
对列表中的每一条记录,再执行一个联合 select 语句来加载每条记录更加详细的信息
(“N”)。
这个问题会导致成千上万的SQL 语句的执行,因此并非总是可取的。
上面的例子,MyBatis 可以使用延迟加载这些查询,因此这些查询立马可节省开销。然而,
如果您加载一个列表后立即迭代访问嵌套的数据,这将会调用所有的延迟加载,因此性能会变得
非常糟糕。
鉴于此,这有另外一种方式。
联合嵌套结果集(Nested Results for Association)
您已经在上面看到了一个非常复杂的嵌套联合的例子,接下的演示的例子会更简单一些。我
们把 Blog 和 Author 表联接起来查询,而不是执行分开的查询语句:
blog_id;
title;
Author author
{
id;
username;
password;
email;
bio;
}
}
建议不要使用 Batatis 的自动赋值,这样不能够清晰地知道要映射哪些属性,
并且有时候还不能保证正确地映射数据库检索结果。
译者注:
如:执行一条 SQL 语句获得了10 条记录,这 10 条记录的每一条再执行一条SQL 语句去加载
更详细的信息,这就执行了10+1 次查询。
resultMap
一个可以映射联合嵌套结果集到一个适合的对象视图上的ResultMap 。这是一个
替代的方式去调用另一个 select 语句。它允许您去联合多个表到一个结果集
里。这样的结果集可能包括冗余的、重复的需要分解和正确映射到一个嵌套对象
视图的数据组。简言之,MyBatis 让您把结果映射‘链接’到一起,用来处理嵌
套结果。举个例子会更好理解,例子在表格下方。
MyBatis 3 - User Guide
39
<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>
注意到这个连接(join),要确保所有的别名都是唯一且无歧义的。这使映射容易多了,现
在我们来映射结果集:
<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>
在上面的例子中,您会看到Blog 的作者(“author”)联合一个“authorResult”结果映射
来加载Author 实例。
重点提示:
id 元素在嵌套结果映射中扮演了非常重要的角色,您应该总是指定一个或多个属性
来唯一标识这个结果集。事实上,如果您没有那样做,MyBatis 也会工作,但是会导致严重性能
开销。选择尽量少的属性来唯一标识结果,而使用主键是最明显的选择(即使是复合主键)。
上面的例子使用一个扩展的resultMap 元素来联合映射。这可使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"/>
MyBatis 3 - User Guide
40
<result property="bio" column="author_bio"/>
</association>
</resultMap>
在上面的例子中您已经看到如果处理“一对一”(“ has one”)类型的联合查询。但是对
于“一对多”(“has many”)的情况如果处理呢?这个问题在下一节讨论。
- 浏览: 189261 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
ZZX19880809:
没看到有[x] Received 'hello word!0' ...
rabbitmq学习3:Publish/Subscribe -
ZZX19880809:
根本就没有. 应该输入first message.
rabbitmq学习2:Work Queues -
jiaofuyou:
独孤日日也 写道我亲自试验了并没有实现啊,第一个worker输 ...
rabbitmq学习2:Work Queues -
独孤日日也:
我亲自试验了并没有实现啊,第一个worker输出: Wait ...
rabbitmq学习2:Work Queues -
jiaofuyou:
想问个问题,象这种任务分发的工作队列,你举的例子是一个队列被多 ...
rabbitmq学习2:Work Queues
发表评论
-
mybatis学习教程-8-SelectBuilder
2013-03-13 09:00 2204SelectBuilder 的秘密SelectBuilde ... -
mybatis学习教程-7Java API
2013-03-13 09:01 3134Java API现在您知道如何配置 MyBatis 和生成 ... -
mybatis学习教程-6-动态 SQL
2013-03-13 09:02 1780动态 SQL(Dynamic SQL)MyBatis 最强 ... -
mybatis学习教程-5-Collection 元素
2013-02-28 14:19 1722Collection 元素<collection pro ... -
mybatis学习教程-3-SQL 映射 XML 文件
2013-02-28 14:01 2255SQL 映射 XML 文件MyBatis ... -
mybatis学习教程-2-Mapper XML 配置
2013-02-28 13:58 2110Mapper 实例Mappers 是创建来绑定映射语句的接口, ... -
MyBatis学习教程-1-初识mybatis
2013-02-28 13:54 1457MyBatis 是什么?MyBatis 是一款一流的支持自 ...
相关推荐
每个元素都可以包含`resultMap`(结果映射)、`parameterMap`(参数映射)等子元素,用来描述SQL查询返回的结果集以及输入参数的处理方式。此外,还有`< resultMap>`元素,用于定义复杂结果的映射规则,它可以处理一...
4. 枚举类型映射:可以使用`<discriminator>`元素根据某个字段的值来决定使用哪个ResultMap,常用于处理枚举类型的字段。 5. TypeHandler:自定义数据类型的转换,比如日期、时间、自定义对象等,可以通过Type...
在MyBatis中,通常会将每个Mapper接口对应的XML映射文件单独放在一个文件中,然后在`mybatis-config.xml`中通过`mappers`元素引用这些映射文件。这样可以保持代码的整洁,并方便管理和更新。 4. **自动提示配置**:...
接下来,`mybatis-3-mapper.dtd` 文件则是 MyBatis 的映射文件的文档类型定义(DTD),它定义了 XML 映射文件中的合法元素和属性。映射文件(如 `UserMapper.xml`)是 MyBatis 中实现 SQL 查询和结果映射的地方,其...
3. **结果集映射**:支持自动结果映射,通过配置`<resultMap>`元素,可以轻松处理复杂的一对多、一对一或多对多关系。 4. **SqlSession管理**:提供了更灵活的SqlSession管理,包括自动关闭和事务控制,简化了...
在`mybatis-demo4-resultMap手动映射.zip`这个示例中,我们将深入探讨ResultMap的使用方法。 ResultMap的基本结构包括id、type、column、javaType、jdbcType、property、discriminator等元素。让我们逐一解析这些...
这个文件定义了MyBatis配置文件的元素和属性,它是MyBatis应用的入口点。配置文件通常命名为`mybatis-config.xml`,其中包含了MyBatis运行时的全局设置,如数据源、事务管理器、类型处理器等。例如,你可以在这个...
`mybatis-3-config.dtd` 和 `mybatis-3-mapper.dtd` 是两个关键的DTD(Document Type Definition)文件,它们定义了Mybatis配置文件和映射文件的结构和元素。 首先,`mybatis-3-config.dtd` 文件是Mybatis的全局...
在MyBatis中,`mybatis-3-mapper.dtd`是一个重要的文件,它是Document Type Definition(DTD)文档,用于定义XML文档的结构和元素。DTD对于开发人员在编写mapper.xml文件时提供代码提示和验证功能,确保XML文件的...