<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 类型,若是你想覆盖或获取的 结果值还须要类型控制器。html
关联中不一样的是你须要告诉 MyBatis 如何加载关联。MyBatis 在这方面会有两种不一样的 方式:java
resultMap 属性的结果映射不一样。数据库
属性 | 描述 |
---|---|
property | 映射到列结果的字段或属性。若是匹配的是存在的,和给定名称相同的 property JavaBeans 的属性, 那么就会使用。 不然 MyBatis 将会寻找给定名称的字段。 这两种情形你可使用一般点式的复杂属性导航。好比,你能够这样映射 一 些 东 西 :“ username ”, 或 者 映 射 到 一 些 复 杂 的 东 西 : “address.street.number” 。 |
javaType | 一个 Java 类的彻底限定名,或一个类型别名(参考上面内建类型别名的列 表) 。若是你映射到一个 JavaBean,MyBatis 一般能够判定类型。然而,如 javaType 果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证所需的 行为。 |
jdbcType | 在这个表格以前的所支持的 JDBC 类型列表中的类型。JDBC 类型是仅仅 须要对插入, 更新和删除操做可能为空的列进行处理。这是 JDBC 的须要, jdbcType 而不是 MyBatis 的。若是你直接使用 JDBC 编程,你须要指定这个类型-但 仅仅对可能为空的值。 |
typeHandler | 咱们在前面讨论过默认的类型处理器。使用这个属性,你能够覆盖默认的 typeHandler 类型处理器。 这个属性值是类的彻底限定名或者是一个类型处理器的实现, 或者是类型别名。 |
属性 | 描述 |
---|---|
column | 来自数据库的类名,或重命名的列标签。这和一般传递给 resultSet.getString(columnName)方法的字符串是相同的。 column 注 意 : 要 处 理 复 合 主 键 , 你 可 以 指 定 多 个 列 名 通 过 column= ” {prop1=col1,prop2=col2} ” 这种语法来传递给嵌套查询语 句。这会引发 prop1 和 prop2 以参数对象形式来设置给目标嵌套查询语句。 |
select | 另一个映射语句的 ID,能够加载这个属性映射须要的复杂类型。获取的 在列属性中指定的列的值将被传递给目标 select 语句做为参数。表格后面 有一个详细的示例。 select 注 意 : 要 处 理 复 合 主 键 , 你 可 以 指 定 多 个 列 名 通 过 column= ” {prop1=col1,prop2=col2} ” 这种语法来传递给嵌套查询语 句。这会引发 prop1 和 prop2 以参数对象形式来设置给目标嵌套查询语句。 |
fetchType | Optional. Valid values are lazy and eager. If present, it supersedes the global configuration parameter lazyLoadingEnabled for this mapping. |
示例:编程
<resultMap id="blogResult" type="Blog"> <association property="author" column="author_id" javaType="Author" select="selectAuthor"/> </resultMap> <select id="selectBlog" resultMap="blogResult"> SELECT * FROM BLOG WHERE ID = #{id} </select> <select id="selectAuthor" resultType="Author"> SELECT * FROM AUTHOR WHERE ID = #{id} </select>
咱们有两个查询语句:一个来加载博客,另一个来加载做者,并且博客的结果映射描 述了“selectAuthor”语句应该被用来加载它的 author 属性。app
其余全部的属性将会被自动加载,假设它们的列和属性名相匹配。less
这种方式很简单, 可是对于大型数据集合和列表将不会表现很好。 问题就是咱们熟知的 “N+1 查询问题”。归纳地讲,N+1 查询问题能够是这样引发的:dom
这个问题会致使成百上千的 SQL 语句被执行。这一般不是指望的。ide
MyBatis 能延迟加载这样的查询就是一个好处,所以你能够分散这些语句同时运行的消 耗。然而,若是你加载一个列表,以后迅速迭代来访问嵌套的数据,你会调用全部的延迟加 载,这样的行为多是很糟糕的。post
因此还有另一种方法。性能
属性 | 描述 |
---|---|
resultMap | 这是结果映射的 ID,能够映射关联的嵌套结果到一个合适的对象图中。这 是一种替代方法来调用另一个查询语句。这容许你联合多个表来合成到 resultMap 一个单独的结果集。这样的结果集可能包含重复,数据的重复组须要被分 解,合理映射到一个嵌套的对象图。为了使它变得容易,MyBatis 让你“链 接”结果映射,来处理嵌套结果。一个例子会很容易来仿照,这个表格后 面也有一个示例。 |
columnPrefix | When joining multiple tables, you would have to use column alias to avoid duplicated column names in the ResultSet. Specifying columnPrefix allows you to map such columns to an external resultMap. Please see the example explained later in this section. |
notNullColumn | By default a child object is created only if at least one of the columns mapped to the child's properties is non null. With this attribute you can change this behaviour by specifiying which columns must have a value so MyBatis will create a child object only if any of those columns is not null. Multiple column names can be specified using a comma as a separator. Default value: unset. |
autoMapping | If present, MyBatis will enable or disable auto-mapping when mapping the result to this property. This attribute overrides the global autoMappingBehavior. Note that it has no effect on an external resultMap, so it is pointless to use it with select or resultMap attribute. Default value: unset. |
在上面你已经看到了一个很是复杂的嵌套关联的示例。 下面这个是一个很是简单的示例 来讲明它如何工做。代替了执行一个分离的语句,咱们联合博客表和做者表在一块儿,就像:
<select id="selectBlog" 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="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>
在上面的示例中你能够看到博客的做者关联表明着“authorResult”结果映射来加载做 者实例。
很是重要: 在嵌套据诶过映射中 id 元素扮演了很是重要的角色。应应该一般指定一个 或多个属性,它们能够用来惟一标识结果。实际上就是若是你离开她了,可是有一个严重的 性能问题时 MyBatis 仍然能够工做。选择的属性越少越好,它们能够惟一地标识结果。主键 就是一个显而易见的选择(尽管是联合主键)。
如今,上面的示例用了外部的结果映射元素来映射关联。这使得 Author 结果映射能够 重用。然而,若是你不须要重用它的话,或者你仅仅引用你全部的结果映射合到一个单独描 述的结果映射中。你能够嵌套结果映射。这里给出使用这种方式的相同示例:
<resultMap id="blogResult" type="Blog"> <id property="id" column="blog_id" /> <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"/> </association> </resultMap>
What if the blog has a co-author? The select statement would look like:
<select id="selectBlog" resultMap="blogResult"> select B.id as blog_id, B.title as blog_title, 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, CA.id as co_author_id, CA.username as co_author_username, CA.password as co_author_password, CA.email as co_author_email, CA.bio as co_author_bio from Blog B left outer join Author A on B.author_id = A.id left outer join Author CA on B.co_author_id = CA.id where B.id = #{id} </select>
Recall that the resultMap for Author is defined as follows.
<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>
Because the column names in the results differ from the columns defined in the resultMap, you need to specify columnPrefix to reuse the resultMap for mapping co-author results.
<resultMap id="blogResult" type="Blog"> <id property="id" column="blog_id" /> <result property="title" column="blog_title"/> <association property="author" resultMap="authorResult" /> <association property="coAuthor" resultMap="authorResult" columnPrefix="co_" /> </resultMap>
上面你已经看到了如何处理“有一个”类型关联。可是“有不少个”是怎样的?下面这 个部分就是来讨论这个主题的。
<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="id" ofType="Post" select="selectPostsForBlog"/> </resultMap> <select id="selectBlog" resultMap="blogResult"> SELECT * FROM BLOG WHERE ID = #{id} </select> <select id="selectPostsForBlog" resultType="Blog"> SELECT * FROM POST WHERE BLOG_ID = #{id} </select>
这里你应该注意不少东西,但大部分代码和上面的关联元素是很是类似的。首先,你应 该注意咱们使用的是集合元素。而后要注意那个新的“ofType”属性。这个属性用来区分 JavaBean(或字段)属性类型和集合包含的类型来讲是很重要的。因此你能够读出下面这个 映射:
<collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectPostsForBlog"/>
读做: “在 Post 类型的 ArrayList 中的 posts 的集合。”
javaType 属性是不须要的,由于 MyBatis 在不少状况下会为你算出来。因此你能够缩短 写法:
<collection property="posts" column="id" ofType="Post" select="selectPostsForBlog"/>
至此,你能够猜想集合的嵌套结果是如何来工做的,由于它和关联彻底相同,除了它应 用了一个“ofType”属性
First, let's look at the 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>
一样,要记得 id 元素的重要性,若是你不记得了,请阅读上面的关联部分。
一样, 若是你引用更长的形式容许你的结果映射的更多重用, 你可使用下面这个替代 的映射:
<resultMap id="blogResult" type="Blog"> <id property="id" column="blog_id" /> <result property="title" column="blog_title"/> <collection property="posts" ofType="Post" resultMap="blogPostResult" columnPrefix="post_"/> </resultMap> <resultMap id="blogPostResult" type="Post"> <id property="id" column="id"/> <result property="subject" column="subject"/> <result property="body" column="body"/> </resultMap>
注意 这个对你所映射的内容没有深度,广度或关联和集合相联合的限制。当映射它们 时你应该在大脑中保留它们的表现。 你的应用在找到最佳方法前要一直进行的单元测试和性 能测试。好在 myBatis 让你后来能够改变想法,而不对你的代码形成很小(或任何)影响。
高级关联和集合映射是一个深度的主题。文档只能给你介绍到这了。加上一点联系,你 会很快清楚它们的用法。