2、ype="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
3、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,
4、 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}
5、ct>
您可能想要把它映射到一个智能的对象模型,包括由一个作者写的一个博客,有许多文章(Post,帖子),每个文章由0个或者多个评论和标签。下面是一个复杂ResultMap 的完整例子(假定作者、博客、文章、评论和标签都是别名)。仔细看看这个例子,但是不用太担心,我们会一步步地来分析,一眼看上去可能让人沮丧,但是实际上非常简单的
[java]
6、avaType="int"/>
7、>
这个resultMap 的元素的子元素比较多,讨论起来比较宽泛。下面我们从概念上概览一下这个resultMap的元素。
resultMap
·constructor–实例化的时候通过构造器将结果集注入到类中
oidArg– ID 参数; 将结果集标记为ID,以方便全局调用
10、
oarg–注入构造器的结果集
·id–结果集ID,将结果集标记为ID,以方便全局调用
·result–注入一个字段或者javabean属性的结果
·association–复杂类型联合;许多查询结果合成这个类型
o嵌套结果映射– associations能引用自身,或者从其它地方引用
·collection–复杂类型集合
o嵌套结果映射– collections能引用自身,或者从其它地方引用
·discriminator–使用一个结果值以决定使用哪个resultMap
ocase–基于不同值的结果映射
§嵌套结果映射–case也能引用它自身, 所以也能包含这些同样的元
11、素。它也可以从外部引用resultMap
è最佳实践:逐步地生成resultMap,单元测试对此非常有帮助。如果您尝试一下子就生成像上面这样巨大的resultMap,可能会出错,并且工作起来非常吃力。从简单地开始,再一步步地扩展,并且进行单元测试。使用框架开发有一个缺点,它们有时像是一个黑合。为了确保达到您所预想的行为,最好的方式就是进行单元测试。这对提交bugs 也非常有用。
下一节,我们一步步地查看这些细节。
元素
这是最基本的结果集映射。id 和result 将列映射到属性或简单的数据类型字段(String, int, double, Date等)。
这两者唯一不同的是,在比较对象实例时id 作为结果集的标识属性。这有助于提高总体性能,特别是应用缓存和嵌套结果映射的时候。
Id、result属性如下:
Attribute
Description
property
映射数据库列的字段或属性。如果JavaBean的属性与给定的名称匹配,就会使用匹配的名字。否则,MyBatis将搜索给定名称的字段。两种情况下您都可以使用逗点的属性形式。比如,您可以映射
13、到“username”,也可以映射到“address.street.number”。
column
数据库的列名或者列标签别名。与传递给resultSet.getString(columnName)的参数名称相同。
javaType
完整java类名或别名(参考上面的内置别名列表)。如果映射到一个JavaBean,那MyBatis通常会自行检测到。然而,如果映射到一个HashMap,那您应该明确指定javaType来确保所需行为。
jdbcType
这张表下面支持的JDBC类型列表列出的JDBC类型。这个属性只在insert,update或delete的时候针对允许空的列有用。JD
14、BC需要这项,但MyBatis不需要。如果您直接编写JDBC代码,在允许为空值的情况下需要指定这个类型。
typeHandler
我们已经在文档中讨论过默认类型处理器。使用这个属性可以重写默认类型处理器。它的值可以是一个TypeHandler实现的完整类名,也可以是一个类型别名。
MyBatis支持如下的JDBC类型:
BIT
FLOAT
CHAR
TIMESTAMP
OTHER
UNDEFINED
TINYINT
REAL
VARCHAR
BINARY
BLOB
NVARCHAR
SMALLINT
DOUBLE
LONGVARCHAR
VARBINA
15、RY
CLOB
NCHAR
INTEGER
NUMERIC
DATE
LONGVARBINARY
BOOLEAN
NCLOB
BIGINT
DECIMAL
TIME
NULL
CURSOR
元素
当属性与DTO,或者与您自己的域模型一起工作的时候,许多场合要用到不变类。通常,包含引用,或者查找的数据很少或者数据不会改变的的表,
16、适合映射到不变类中。构造器注入允许您在类实例化后给类设值,这不需要通过public方法。MyBatis同样也支持private属性和JavaBeans的私有属性达到这一点,但是一些用户可能更喜欢使用构造器注入。构造器元素可以做到这点。
考虑下面的构造器:
public class User {
//…
public User(int id, String username) {
//…
}
//…
}
为了将结果注入构造器,MyBatis需要使用它的参数类型来标记构造器。Java没有办法通过参数名称来反射获得。因此当创建constructor 元素,确保参数
17、是按顺序的并且指定了正确的类型。
其它的属性与规则与id、result元素的一样。
Attribute
Description
column
数据库的列名或者列标签别名。与传递给resultSet.getString(columnName)的参数名称相同。
javaType
完整java类名或别名(参考上面的内置别名列表)。如果映射到一个Ja
18、vaBean,那MyBatis通常会自行检测到。然而,如果映射到一个HashMap,那您应该明确指定javaType来确保所需行为。
jdbcType
支持的JDBC类型列表中列出的JDBC类型。这个属性只在insert,update或delete的时候针对允许空的列有用。JDBC需要这项,但MyBatis不需要。如果您直接编写JDBC代码,在允许为空值的情况下需要指定这个类型。
typeHandler
我们已经在文档中讨论过默认类型处理器。使用这个属性可以重写默认类型处理器。它的值可以是一个TypeHandler实现的完整类名,也可以是一个类型别名。
元素
19、n property="author" column="blog_author_id" javaType=" Author">
Association元素处理“has-one”(一对一)这种类型关系。比如在我们的例子中,一个Blog有一个Author。联合映射与其它的结果集映射工作方式差不多,指定property、column、javaType(通常MyBatis会自动识
20、别)、jdbcType(如果需要)、typeHandler。
不同的地方是您需要告诉MyBatis 如何加载一个联合查询。MyBatis使用两种方式来加载:
·Nested Select:通过执行另一个返回预期复杂类型的映射SQL语句(即引用外部定义好的SQL语句块)。
·Nested Results:通过嵌套结果映射(nested result mappings)来处理联接结果集(joined results)的重复子集。
首先,让我们检查一下元素属性。正如您看到的,它不同于普通只有select和resultMap属性的结果映射。
Attribute
Description
21、
property
映射数据库列的字段或属性。如果JavaBean的属性与给定的名称匹配,就会使用匹配的名字。否则,MyBatis将搜索给定名称的字段。两种情况下您都可以使用逗点的属性形式。比如,您可以映射到”username”,也可以映射到更复杂点的”address.street.number”。
column
数据库的列名或者列标签别名。与传递给resultSet.getString(columnName)的参数名称相同。
注意: 在处理组合键时,您可以使用column= “{prop1=col1,prop2=col2}”这样的语法,设置多个列名传入到嵌套查询语句。这就会把prop
22、1和prop2设置到目标嵌套选择语句的参数对象中。
javaType
完整java类名或别名(参考上面的内置别名列表)。如果映射到一个JavaBean,那MyBatis通常会自行检测到。然而,如果映射到一个HashMap,那您应该明确指定javaType来确保所需行为。
jdbcType
支持的JDBC类型列表中列出的JDBC类型。这个属性只在insert,update或delete的时候针对允许空的列有用。JDBC需要这项,但MyBatis不需要。如果您直接编写JDBC代码,在允许为空值的情况下需要指定这个类型。
typeHandler
我们已经在文档中讨论过默认类型处理器。使用
23、这个属性可以重写默认类型处理器。它的值可以是一个TypeHandler实现的完整类名,也可以是一个类型别名。
联合嵌套选择(Nested Select for Association)
select
通过这个属性,通过ID引用另一个加载复杂类型的映射语句。从指定列属性中返回的值,将作为参数设置给目标select语句。表格下方将有一个例子。注意:在处理组合键时,您可以使用column=”{prop1=col1,prop2=col2}”这样的语法,设置多个列名传入到嵌套语句。这就会把prop1和prop2设置到目标嵌套语句的参数对象中。
例如:
[html]
24、 id=”blogResult” type=”Blog”>
SELECT * FROM BLOG WHERE ID = #{id} 25、nt” resultType="Author"> SELECT * FROM AUTHOR WHERE ID = #{id}
我们使用两个select语句:一个用来加载Blog,另一个用来加载Author。Blog的resultMap 描述了使用“selectAuthor”语句来加载author的属性。
如果列名和属性名称相匹配的话,所有匹配的属性都会自动加载。
译者注:
上面的例子,首先执行,执行结果存放到结果映射中。“blogResult”是一个
26、Blog类型,从查出的数据都会自动赋值给”blogResult”的与列名匹配的属性,这时blog_id,title等就被赋值了。同时“blogResult”还有一个关联属性"Author",执行嵌套查询select=”selectAuthor”后,Author对象的属性id,username,password,email,bio也被赋于数据库匹配的值。
Blog
{
blog_id;
title;
Author author
{
id;
username;
password;
email;
bio;
}
27、
}
虽然这个方法简单,但是对于大数据集或列表查询,就不尽如人意了。这个问题被称为“N+1 选择问题”(N+1 Selects Problem)。概括地说,N+1选择问题是这样产生的:
·您执行单条SQL语句去获取一个列表的记录( “+1”)。
·对列表中的每一条记录,再执行一个联合select 语句来加载每条记录更加详细的信息(“N”)。
这个问题会导致成千上万的SQL语句的执行,因此并非总是可取的。
上面的例子,MyBatis可以使用延迟加载这些查询,因此这些查询立马可节省开销。然而,如果您加载一个列表后立即迭代访问嵌套的数据,这将会调用所有的延迟加载,因此性能
28、会变得非常糟糕。
鉴于此,这有另外一种方式。
联合嵌套结果集(Nested Results for Association)
resultMap
一个可以映射联合嵌套结果集到一个适合的对象视图上的ResultMap。这是一个替代的方式去调用另一个select语句。它允许您去联合多个表到一个结果集里。这样的结果集可能包括冗余的、重复的需要分解和正确映射到一个嵌套对象视图的数据组。简言之,MyBatis让您把结果映射‘链接’到一起,用来处理嵌套结果。举个例子会更好理解,例子在表格下方。
您已经在上面看到了一个非常复杂的嵌套联合的例子,接下的演示的例子会更简单一些。我们把Blog和Aut
29、hor表联接起来查询,而不是执行分开的查询语句:
[html]
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 autho
30、r_bio from Blog B left outer join Author A on B.author_id = A.id where B.id = #{id}
注意到这个连接(join),要确保所有的别名都是唯一且无歧义的。这使映射容易多了,现在我们来映射结果集:
[html]
31、erty="author" column="blog_author_id" javaType="Author" resultMap=”authorResult”/>