resultMap 元素是 MyBatis 中最重要最强大的元素。它就是让你远离从结果集中取出数据的JDBC 代码的那个东西,并且在一些情形下容许你作一些JDBC不支持的事情。ResultMap 的设计就是对于一些简单的语句咱们不须要明确它们的结果映射,可是到于复杂的语句确实须要描述它们的关系。 java
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; } } 基于 JavaBean 的规范,上面这个类有 3 个属性:id,username 和 hashedPassword。这些 在 select 语句中会精确匹配到列名。 这样的一个 JavaBean 能够被映射到结果集 : |
select id, username, hashedPassword from some_table where id = #{id} </select> 这些状况下,MyBatis 会在幕后自动建立一个 ResultMap,基于属性名来映射列到JavaBean 的属性上。若是列名没有精确匹配,你能够在列名上使用 select 字句的别名(一个基本的SQL特性)来匹配标签。好比: |
select user_id as id, user_name as username, hashed_Password as hashedPassword from some_table where id = #{id} </select> 还能够经过下面的这种方式来解决列名和属性名不一致的状况: |
<id property="id" column="user_id" /> <result property="username" column="user_name"/> <result property="hashedPassword" column="hashed_password"/> </resultMap> 引用它的语句使用 resultMap 属性就好了(注意咱们去掉了resultType 属性,这两个属性只能包含其中的一个) <select id=”selectUsers” parameterType=”int” resultMap=”userResultMap”> select id, username, hashedPassword from some_table where id = #{id} </select> |
2.高级结果映射 数据库
对于下面的这个语句子咱们如何进行映射呢? 编程
<!-- Very Complex Statement --> 缓存
<select id="selectBlogDetails" parameterType="int" resultMap="detail edBl ogRes ultMa p"> app select dom B.id as blog_id, post B.title as blog_title, 性能 B.author_id as blog_author_id, this A.id as author_id, spa 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> |
将它映射到一个对象模型,其中包含一个做者写的博客,有不少的博文,每篇博文有零条或多条的评论和标签。下面是一个完整的复杂结果映射例子(假设做者,博客, 博文,评论和标签都是类型的别名)
<!-- 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 元素有不少子元素和一个值得讨论的结构。下面是 resultMap 元素的概念视图 resultMap
2.1 id,result
id 和 result 都映射一个单独列的值到简单数据类型(字符串,整型,双精度浮点数,日期等)的单独属性或字段。这二者之间的惟一不一样是 id 表示的结果将是当比较对象实例时用到的标识属性。这帮助来改进总体表现,特别是缓存和嵌入结果映射(也就是联合映射)。 每一个都有一些属性:
属性 |
描述 |
property |
映射到列结果的字段或属性。若是匹配的是存在的, 和给定名称相同的JavaBeans 的属性,那么就会使用。不然 MyBatis 将会寻找给定名称的字段。这两种情形你可使用一般点式的复杂属性导航。好比,你能够这样映射一些东西:“username”,或者映射到一些复杂的东西: “address.street.number”。 |
column |
从数据库中获得的列名 ,或者是列名的重命名标签。 这也是一般和会传递给 resultSet.getString(columnName)方法参数中相同的字符串。 |
javaType |
一个 Java 类的彻底限定名,或一个类型别名(参加上面内建类型别名 的列表)。若是你映射到一个 JavaBean,MyBatis 一般能够判定类型。 然而,若是你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证所需的行为。 |
jdbcType |
在这个表格以后的所支持的 JDBC 类型列表中的类型。JDBC 类型是仅 仅须要对插入,更新和删除操做可能为空的列进行处理。这是 JDBC 的须要,而不是 MyBatis 的。若是你直接使用 JDBC 编程,你须要指定 这个类型-但仅仅对可能为空的值。 |
typeHandler |
使用这个属性 ,你能够覆盖默认的类型处理器。这个属性值是类的彻底限定名或者 是一个类型处理器的实现,或者是类型别名。 |
2.2构造方法
<constructor >
<idArg column="id" javaType="int"/>
<arg column=”username” javaType=”String”/>
</constructor>
看看下面这个构造方法:
public class User { //...
public User(int id, String username) { //...
}
//... }
为了向这个构造方法中注入结果,MyBatis 须要经过它的参数的类型来标识构造方法。 Java 没有自查(反射)参数名的方法。因此当建立一个构造方法元素时,保证参数是按顺序 排列的,并且数据类型也是肯定的。
<constructor >
<idArg column="id" javaType="int"/>
<arg column=”username” javaType=”String”/>
</constructor>
剩余的属性和规则和固定的 id 和 result 元素是相同的。
2.3 关联
<association property="author" column="blog_author_id" javaType=" Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
</association>
关联元素处理“有一个”类型的关系。好比,在咱们的示例中,一个博客有一个用户。 关联映射就工做于这种结果之上。你指定了目标属性,来获取值的列,属性的 java 类型(很 多状况下 MyBatis 能够本身算出来),若是须要的话还有 jdbc 类型,若是你想覆盖或获取的结果值还须要类型控制器。
关联中不一样的是你须要告诉 MyBatis 如何加载关联。MyBatis 在这方面会有两种不一样的 方式:
嵌套查询:经过执行另一个 SQL 映射语句来返回预期的复杂类型。
嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集。
首先,让咱们来查看这个元素的属性。它和普通的只由 select 和resultMap 属性的结果映射不一样。
property |
映射到列结果的字段或属性。若是匹配的是存在的,和给定名称相同的 JavaBeans 的属性,那么就会使用。不然 MyBatis 将会寻找给定名称的字段。 这两种情形你可使用一般点式的复 杂属性导航。好比,你能够 这样映射 一 些 东 西 :“ username ”, 或 者 映 射 到 一 些 复 杂 的 东 西 : “address.street.number”。 |
column |
来自数据库的类名,或重命名的列标签。这和一般传递给 resultSet.getString(columnName)方法的字符串是相同的。 注意:要处理复合主键,你能够指定多个列名经过 column=” {prop1=col1,prop2=col2}”这种语法来传递给嵌套查询语句。这会引发 prop1 和 prop2 以参数对象形式来设置给目标嵌套查询语句。 |
javaType |
一个 Java 类的彻底限定名,或一个类型别名(参加上面内建类型别名的列 表)。若是你映射到一个 JavaBean,MyBatis 一般能够判定类型。然而,如 果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证所需的 行为。 |
jdbcType |
在这个表格以前的所支持的 JDBC 类型列表中的类型。JDBC 类型是仅仅 须要对插入,更新和删除操做可能为空的列进行处理。这是 JDBC 的须要, 而不是 MyBatis 的。若是你直接使用 JDBC 编程,你须要指定这个类型-但 仅仅对可能为空的值。 |
typeHandler |
咱们在前面讨论过默认的类型处理器 。使用这个属性,你能够覆 盖默认的 类型处理器。这个属性值是类的彻底限定名或者是一个类型处理器的实现, 或者是类型别名。 |
关联的嵌套查询
select |
另一个映射语句的 ID,能够加载这个属性映射须要的复杂类型。获取的 在列属性中指定的列的值将被传递给目标 select 语句做为参数。表格后面 有一个详细的示例。 注意:要处理复合主键,你能够指定多个列名经过 column=” {prop1=col1,prop2=col2}”这种语法来传递给嵌套查询语句。这会引发 prop1 和 prop2 以参数对象形式来设置给目标嵌套查询语句。 |
示例:
<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> |
咱们有两个查询语句:一个来加载博客,另一个来加载做者,并且博客的结果映射描述了“selectAuthor”语句应该被用来加载它的 author 属性。其余全部的属性将会被自动加载,假设它们的列和属性名相匹配。
这种方式很简单,可是对于大型数据集合和列表将不会表现很好。问题就是咱们熟知的 “N+1 查询问题”。
因此还有另一种方法。
关联的嵌套结果
resultMap |
这是结果映射的ID,能够映射关联的嵌套结果到一个合适的对象图中。这是一种替代方法来调用另一个查询语句。这容许你联合多个表 来合成到一个单独的结果集。这样的结果集可能包含重复,数据的重复组须要被分解,合理映射到一个嵌套的对象图。为了使它变得容易,MyBatis 让你“连接”结果映射,来处理嵌套结果。 |
在上面你已经看到了一个很是复杂的嵌套关联的示例。下面这个是一个很是简单的示例 来讲明它如何工做。代替了执行一个分离的语句,咱们联合博客表和做者表在一块儿,就像:
<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> |
如今咱们能够映射这个结果:
<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" 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> |
在嵌套据诶过映射中 id 元素扮演了很是重要的角色。应应该一般指定一个 或多个属性,它们能够用来惟一标识结果。实际上就是若是你离开她了,可是有一个严重的 性能问题时 MyBatis 仍然能够工做。选择的属性越少越好,它们能够惟一地标识结果。主键就是一个显而易见的选择(尽管是联合主键)。
如今,上面的示例用了外部的结果映射元素来映射关联。这使得 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"/> <result property="bio" column="author_bio"/> </association> </resultMap> |
2.4集合
<collection property="posts" ofType="domain.blog.Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<result property="body" column="post_body"/>
</collection >
集合元素的做用几乎和关联是相同的。咱们来继续上面的示例,一个博客只有一个做者。可是博客有不少文章。在博客类中, 这能够由下面这样的写法来表示:
private List<Post> posts;
要映射嵌套结果集合到 List 中,咱们使用集合元素。就像关联元素同样,咱们能够从 链接中使用嵌套查询,或者嵌套结果。
集合的嵌套查询
首先,让咱们看看使用嵌套查询来为博客加载文章。
<resultMap id=”blogResult” type=”Blog”> <collection property="posts" javaType=”ArrayList” column="blog_id" ofType="Post" select=”selectPostsForBlog”/> </resultMap> <select id=”selectBlog” parameterType=”int” resultMap=”blogResult”> SELECT * FROM BLOG WHERE ID = #{id} </select> <select id=”selectPostsForBlog” parameterType=”int” resultType="Author"> SELECT * FROM POST WHERE BLOG_ID = #{id} </select> |
这里你应该注意不少东西,但大部分代码和上面的关联元素是很是类似的。首先,你应 该注意咱们 使用的是 集合元素 。而后要 注意那个 新的“of Ty pe ”属性。这 个属性用 来区分 JavaBean(或字段)属性类型和集合包含的类型来讲是很重要的。因此你能够读出下面这个 映射:
<collection property="posts" javaType=”ArrayList” column="blog_id" ofType="Post" select=”selectPostsForBlog”/>
读做:“在 Post 类型的 ArrayList 中的 posts 的集合。”
javaType 属性是不须要的,由于 MyBatis 在不少状况下会为你算出来。因此你能够缩短 写法:
<collection property="posts" column="blog_id" ofType="Post" select=”selectPostsForBlog”/>
集合的嵌套结果
至此,你能够猜想集合的嵌套结果是如何来工做的,由于它和关联彻底相同,除了它应 用了一个“ofType ”属性
<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, 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> |
一样,若是你引用更长的形式容许你的结果映射的更多重用,你可使用下面这个替代的映射:
<resultMap id="blogResult" type="Blog"> <id property=”id” column="blog_id" /> <result property="title" column="blog_title"/> <collection property="posts" ofType="Post" resultMap=”blogPostResult”/> </resultMap> <resultMap id="blogPostResult" type="Post"> <id property="id" column="post_id"/> <result property="subject" column="post_subject"/> <result property="body" column="post_body"/> </resultMap> |