mysql xml映射文件中的_Mybatis学习—XML映射文件

insert into Author (username, password, email, bio) values(#{item.username}, #{item.password}, #{item.email}, #{item.bio})

Result Maps

resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来, 并在一些情形下允许你做一些 JDBC 不支持的事情。 实际上,在对复杂语句进行联合映射的时候,它很可能可以代替数千行的同等功能的代码。 ResultMap 的设计思想是,简单的语句不需要明确的结果映射,而复杂一点的语句只需要描述它们的关系就行了。

你已经见过简单映射语句的示例了,但没有明确的 resultMap。比如:

selectid, username, hashedPasswordfromsome_tablewhere id =#{id}

上述语句只是简单地将所有的列映射到 HashMap 的键上,这由 resultType 属性指定。 虽然在大部分情况下都够用,但是 HashMap 不是一个很好的领域模型。 你的程序更可能会使用 JavaBean 或 POJO(Plain Old Java Objects,普通 Java 对象)作为领域模型。 MyBatis 对两者都支持。看看下面这个 JavaBean:

package com.someapp.model;public classUser {private intid;privateString username;privateString hashedPassword;public intgetId() {returnid;

}public void setId(intid) {this.id =id;

}publicString getUsername() {returnusername;

}public voidsetUsername(String username) {this.username =username;

}publicString getHashedPassword() {returnhashedPassword;

}public voidsetHashedPassword(String hashedPassword) {this.hashedPassword =hashedPassword;

}

}

基于 JavaBean 的规范,上面这个类有 3 个属性:id,username 和 hashedPassword。这些属性会对应到 select 语句中的列名。

这样的一个 JavaBean 可以被映射到 ResultSet,就像映射到 HashMap 一样简单。

selectid, username, hashedPasswordfromsome_tablewhere id =#{id}

类型别名是你的好帮手。使用它们,你就可以不用输入类的完全限定名称了。比如:

selectid, username, hashedPasswordfromsome_tablewhere id =#{id}

这些情况下,MyBatis 会在幕后自动创建一个 ResultMap,再基于属性名来映射列到 JavaBean 的属性上。如果列名和属性名没有精确匹配,可以在 SELECT 语句中对列使用别名(这是一个 基本的 SQL 特性)来匹配标签。比如:

selectuser_idas "id",

user_nameas "userName",

hashed_passwordas "hashedPassword"

fromsome_tablewhere id =#{id}

ResultMap 最优秀的地方在于,虽然你已经对它相当了解了,但是根本就不需要显式地用到他们。 上面这些简单的示例根本不需要下面这些繁琐的配置。 出于示范的原因,让我们来看看最后一个示例中,如果使用外部的 resultMap 会怎样,这也是解决列名不匹配的另外一种方式。

引用它的语句使用 resultMap 属性就行了(注意我们去掉了 resultType 属性)。比如:

selectuser_id, user_name, hashed_passwordfromsome_tablewhere id =#{id}

如果世界总是这么简单就好了。

关联

关联元素处理“有一个”类型的关系。比如,在我们的示例中,一个博客有一个用户。 关联映射就工作于这种结果之上。你指定了目标属性,来获取值的列,属性的 java 类型(很 多情况下 MyBatis 可以自己算出来) ,如果需要的话还有 jdbc 类型,如果你想覆盖或获取的 结果值还需要类型控制器。

关联中不同的是你需要告诉 MyBatis 如何加载关联。MyBatis 在这方面会有两种不同的 方式:

嵌套查询:通过执行另外一个 SQL 映射语句来返回预期的复杂类型。

嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集。首先,然让我们来查看这个元素的属性。所有的你都会看到,它和普通的只由 select 和

resultMap 属性的结果映射不同。

属性描述

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

可选的。有效值为 lazy和eager。 如果使用了,它将取代全局配置参数lazyLoadingEnabled。

示例:

SELECT * FROM BLOG WHERE ID = #{id}

SELECT * FROM AUTHOR WHERE ID = #{id}

我们有两个查询语句:一个来加载博客,另外一个来加载作者,而且博客的结果映射描 述了“selectAuthor”语句应该被用来加载它的 author 属性。

其他所有的属性将会被自动加载,假设它们的列和属性名相匹配。

这种方式很简单, 但是对于大型数据集合和列表将不会表现很好。 问题就是我们熟知的 “N+1 查询问题”。概括地讲,N+1 查询问题可以是这样引起的:

你执行了一个单独的 SQL 语句来获取结果列表(就是“+1”)。

对返回的每条记录,你执行了一个查询语句来为每个加载细节(就是“N”)。

这个问题会导致成百上千的 SQL 语句被执行。这通常不是期望的。

MyBatis 能延迟加载这样的查询就是一个好处,因此你可以分散这些语句同时运行的消 耗。然而,如果你加载一个列表,之后迅速迭代来访问嵌套的数据,你会调用所有的延迟加 载,这样的行为可能是很糟糕的。

所以还有另外一种方法。

关联的嵌套结果

属性描述

resultMap

这是结果映射的 ID,可以映射关联的嵌套结果到一个合适的对象图中。这 是一种替代方法来调用另外一个查询语句。这允许你联合多个表来合成到 resultMap 一个单独的结果集。这样的结果集可能包含重复,数据的重复组需要被分 解,合理映射到一个嵌套的对象图。为了使它变得容易,MyBatis 让你“链 接”结果映射,来处理嵌套结果。一个例子会很容易来仿照,这个表格后 面也有一个示例。

columnPrefix

当连接多表时,你将不得不使用列别名来避免ResultSet中的重复列名。指定columnPrefix允许你映射列名到一个外部的结果集中。 请看后面的例子。

notNullColumn

默认情况下,子对象仅在至少一个列映射到其属性非空时才创建。 通过对这个属性指定非空的列将改变默认行为,这样做之后Mybatis将仅在这些列非空时才创建一个子对象。 可以指定多个列名,使用逗号分隔。默认值:未设置(unset)。

autoMapping

如果使用了,当映射结果到当前属性时,Mybatis将启用或者禁用自动映射。 该属性覆盖全局的自动映射行为。 注意它对外部结果集无影响,所以在select or resultMap属性中这个是毫无意义的。 默认值:未设置(unset)。

在上面你已经看到了一个非常复杂的嵌套关联的示例。 下面这个是一个非常简单的示例 来说明它如何工作。代替了执行一个分离的语句,我们联合博客表和作者表在一起,就像:

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}

注意这个联合查询, 以及采取保护来确保所有结果被唯一而且清晰的名字来重命名。 这使得映射非常简单。现在我们可以映射这个结果:

在上面的示例中你可以看到博客的作者关联代表着“authorResult”结果映射来加载作 者实例。

非常重要: id元素在嵌套结果映射中扮演着非 常重要的角色。你应该总是指定一个或多个可以唯一标识结果的属性。实际上如果你不指定它的话, MyBatis仍然可以工作,但是会有严重的性能问题。在可以唯一标识结果的情况下, 尽可能少的选择属性。主键是一个显而易见的选择(即使是复合主键)。

现在,上面的示例用了外部的结果映射元素来映射关联。这使得 Author 结果映射可以 重用。然而,如果你不需要重用它的话,或者你仅仅引用你所有的结果映射合到一个单独描 述的结果映射中。你可以嵌套结果映射。这里给出使用这种方式的相同示例:

如果blog有一个co-author怎么办? select语句将看起来这个样子:

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}

再次调用Author的resultMap将定义如下:

因为结果中的列名与resultMap中的列名不同。 你需要指定columnPrefix去重用映射co-author结果的resultMap。

上面你已经看到了如何处理“有一个”类型关联。但是“有很多个”是怎样的?下面这 个部分就是来讨论这个主题的。

集合

集合元素的作用几乎和关联是相同的。实际上,它们也很相似,文档的异同是多余的。 所以我们更多关注于它们的不同。

我们来继续上面的示例,一个博客只有一个作者。但是博客有很多文章。在博客类中, 这可以由下面这样的写法来表示:

privateListposts;

要映射嵌套结果集合到 List 中,我们使用集合元素。就像关联元素一样,我们可以从 连接中使用嵌套查询,或者嵌套结果。

集合的嵌套查询

首先,让我们看看使用嵌套查询来为博客加载文章。

SELECT * FROM BLOG WHERE ID = #{id}

SELECT * FROM POST WHERE BLOG_ID = #{id}

这里你应该注意很多东西,但大部分代码和上面的关联元素是非常相似的。首先,你应 该注意我们使用的是集合元素。然后要注意那个新的“ofType”属性。这个属性用来区分 JavaBean(或字段)属性类型和集合包含的类型来说是很重要的。所以你可以读出下面这个 映射:

读作: “在 Post 类型的 ArrayList 中的 posts 的集合。”

javaType 属性是不需要的,因为 MyBatis 在很多情况下会为你算出来。所以你可以缩短 写法:

集合的嵌套结果

至此,你可以猜测集合的嵌套结果是如何来工作的,因为它和关联完全相同,除了它应 用了一个“ofType”属性

首先, 让我们看看 SQL:

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}

我们又一次联合了博客表和文章表,而且关注于保证特性,结果列标签的简单映射。现 在用文章映射集合映射博客,可以简单写为:

同样,要记得 id 元素的重要性,如果你不记得了,请阅读上面的关联部分。

同样, 如果你引用更长的形式允许你的结果映射的更多重用, 你可以使用下面这个替代 的映射:

注意 这个对你所映射的内容没有深度,广度或关联和集合相联合的限制。当映射它们 时你应该在大脑中保留它们的表现。 你的应用在找到最佳方法前要一直进行的单元测试和性 能测试。好在 myBatis 让你后来可以改变想法,而不对你的代码造成很小(或任何)影响。

高级关联和集合映射是一个深度的主题。文档只能给你介绍到这了。加上一点联系,你 会很快清楚它们的用法。

自动映射

正如你在前面一节看到的,在简单的场景下,MyBatis可以替你自动映射查询结果。 如果遇到复杂的场景,你需要构建一个result map。 但是在本节你将看到,你也可以混合使用这两种策略。 让我们到深一点的层面上看看自动映射是怎样工作的。

当自动映射查询结果时,MyBatis会获取sql返回的列名并在java类中查找相同名字的属性(忽略大小写)。 这意味着如果Mybatis发现了ID列和id属性,Mybatis会将ID的值赋给id。

通常数据库列使用大写单词命名,单词间用下划线分隔;而java属性一般遵循驼峰命名法。 为了在这两种命名方式之间启用自动映射,需要将 mapUnderscoreToCamelCase设置为true。

自动映射甚至在特定的result map下也能工作。在这种情况下,对于每一个result map,所有的ResultSet提供的列, 如果没有被手工映射,则将被自动映射。自动映射处理完毕后手工映射才会被处理。 在接下来的例子中, id 和 userName列将被自动映射, hashed_password 列将根据配置映射。

selectuser_idas "id",

user_nameas "userName",

hashed_passwordfromsome_tablewhere id =#{id}

有三种自动映射等级:

NONE - 禁用自动映射。仅设置手动映射属性。

PARTIAL - 将自动映射结果除了那些有内部定义内嵌结果映射的(joins).

FULL - 自动映射所有。

默认值是PARTIAL,这是有原因的。当使用FULL时,自动映射会在处理join结果时执行,并且join取得若干相同行的不同实体数据,因此这可能导致非预期的映射。下面的例子将展示这种风险:

selectB.id,

B.title,

A.username,from Blog B left outer join Author A on B.author_id =A.idwhere B.id =#{id}

在结果中Blog和Author均将自动映射。但是注意Author有一个id属性,在ResultSet中有一个列名为id, 所以Author的id将被填充为Blog的id,这不是你所期待的。所以需要谨慎使用FULL。

通过添加autoMapping属性可以忽略自动映射等级配置,你可以启用或者禁用自动映射指定的ResultMap。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值