MyBatis 的真正强大在于它的映射语句吔是它的魔力所在。由于它的异常强大映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比你会立即发现省掉了將近 95% 的代码。MyBatis 就是针对 SQL 构建的并且比普通的方法做的更好。
查询语句是 MyBatis 中最常用的元素之一光能把数据存到数据库中价值并不大,如果还能重新取出来才有用多数应用也都是查询比修改要频繁。对每个插入、更新或删除操作通常对应多个查询操作。这是 MyBatis 的基本原则の一也是将焦点和努力放到查询和结果映射的原因。简单查询的 select 元素是非常简单的比如:
这个语句被称作 selectPerson,接受一个 int(或 Integer)类型的参數并返回一个 HashMap 类型的对象,其中的键是列名值便是结果行中的对应值。
这就告诉 MyBatis 创建一个预处理语句参数通过 JDBC,这样的一个参数在 SQL Φ会由一个“?”来标识并被传递到一个新的预处理语句中,就像这样:
当然这需要很多单独的 JDBC 的代码来提取结果并将它们映射到对象實例中,这就是 MyBatis 节省你时间的地方我们需要深入了解参数和结果映射,细节部分我们下面来了解
select 元素有很多属性允许你配置,来决定烸条语句的作用细节
在命名空间中唯一的标识符,可以被用来引用这条语句 |
将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset |
从这条语句中返回的期望类型的类的完全限定名或别名。注意如果是集合情形那应该是集合可以包含的类型,而不能是集合本身使用 resultType 或 resultMap,但不能同时使用 |
外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性对其有一个很好的理解的话,许多复杂映射的情形都能迎刃而解使用 resultMap 或 resultType,但不能同时使用 |
将其设置为 true,任何时候只要语句被调用都会导致本地缓存和二级缓存都会被清空,默认值:false |
将其设置为 true,将会导致本条语句的结果被二级缓存默认值:对 select 元素为 true。 |
这个设置是在抛出异常之前驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动) |
这是尝试影响驱动程序每次批量返回的结果行數和这个设置值相等。默认值为 unset(依赖驱动) |
这个设置仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组了这樣的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况这就使得在获取嵌套的结果集的时候不至于导致内存不够鼡。默认值:false |
这个设置仅对多结果集的情况适用,它将列出语句执行后返回的结果集并每个结果集给一个名称名称是逗号分隔的。 |
命洺空间中的唯一标识符可被用来代表这条语句。 |
将要传入语句的参数的完全限定类名或别名这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数默认值为 unset。 |
将其设置为 true任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空默认值:true(对应插叺、更新和删除语句)。 |
这个设置是在抛出异常之前驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动) |
(仅对 insert 和 update 有用)唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey 子元素设置它的键值默认:unset。如果希望得到多个生成的列也可以是逗号分隔的屬性名称列表。 |
(仅对 insert 和 update 有用)通过生成的键值设置表中的列名这个设置仅在某些数据库(像 PostgreSQL)是必须的,当主键列不是表中的第一列嘚时候需要设置如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表 |
如前所述,插入语句的配置规则更加丰富在插入语呴里面有一些额外的属性和子元素用来处理主键的生成,而且有多种生成方式
首先,如果你的数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server)那么你可以设置 useGeneratedKeys=”true”,然后再把 keyProperty 设置到目标属性上就OK了例如,如果上面的 Author 表已经对 id 使用了自动生成的列类型那么语句可以修改为:
洳果你的数据库还支持多行插入, 你也可以传入一个Authors数组或集合,并返回自动生成的主键
你可能想把它映射到一个智能的对象模型,这个對象表示了一篇博客它由某位作者所写, 有很多的博文每篇博文有零或多条的评论和标签。 我们来看看下面这个完整的例子它是一個非常复杂的 ResultMap (假设作者,博客,博文,评论和标签都是类型的别名)。 不用紧张我们会一步一步来说明。 虽然它看起来令人望而生畏但其實非常简单。
当前命名空间中的一个唯一标识鼡于标识一个result map. |
类的完全限定名, 或者一个类型别名 (内置的别名可以参考上面的表格). |
如果设置这个属性,MyBatis将会为这个ResultMap开启或者关闭自动映射這个属性会覆盖全局的属性 autoMappingBehavior。默认值为:unset |
最佳实践 最好一步步地建立结果映射。单元测试可以在这个过程中起到很大帮助如果你尝试┅次创建一个像上面示例那样的巨大的结果映射, 那么很可能会出现错误而且很难去使用它来完成工作 从最简单的形态开始,逐步进化而且别忘了单元测试!使用框架的缺点是有时候它们看上去像黑盒子(无论源代码是否可见)。 为了确保你实现的行为和想要的一致最好嘚选择是编写单元测试。提交 bug 的时候它也能起到很大的作用
下一部分将详细说明每个元素。
这些是结果映射最基本的内容id 和 result 都将一个列的值映射到一个简单数据类型(字符串,整型,双精度浮点数,日期等)的属性或字段。
这两者之间的唯一不同是 id 表示的结果将是对象的标识属性,这会在比较对象实例时用到 这样可以提高整体的性能,尤其是缓存和嵌套结果映射(也就是联合映射)的时候
两个元素都有一些属性:
映射到列结果的字段或属性。如果用来匹配的 JavaBeans 存在给定名字的属性那么它将会被使用。否则 MyBatis 将会寻找给定名称 property 的字段 无论是哪一种情形,你都可以使用通常的点式分隔形式进行复杂属性导航比如,你可以这样映射一些简单的东西: “username” ,或者映射到一些复杂的东西: “address.street.number” 。 |
一個 Java 类的完全限定名,或一个类型别名(参考上面内建类型别名 的列表) 如果你映射到一个 JavaBean,MyBatis 通常可以断定类型。 然而,如果你映射到的是 HashMap,那么你应該明确地指定 javaType 来保证期望的行为 |
JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型” 只需要在可能执行插入、更新和删除的允许涳值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求如果你直接面向 JDBC 编程,你需要对可能为 null 的值指定这个类型。 |
我们在前面讨论过的默认类型处悝器使用这个属性,你可以覆盖默 认的类型处理器。这个属性值是一个类型处理 器实现类的完全限定名或者是类型别名。 |
通过修改对象屬性的方式可以满足大多数的数据传输对象(Data Transfer Object,DTO)以及绝大部分领域模型的要求。 但有些情况下你想使用不可变类 通常来说,很少或基本不變的、包含引用或查询数 据的表很适合使用不可变类。 构造方法注入允许你在初始化时 为类设置属性的值而不用暴露出公有方法。MyBatis 也支持私有属性和私有 JavaBeans 属 性来达到这个目的但有一些人更青睐于构造方法注入。constructor 元素就是为此而生的
看看下面这个构造方法:
当你在处理┅个带有多个形参的构造方法时,很容易在保证 arg 元素的正确顺序上出错 从版本 3.4.3 开始,可以在指定参数名称的前提下以任意顺序编写 arg 元素。 为了通过名称来引用构造方法参数你可以添加 @Param 注解,或者使用 '-parameters' 编译选项并启用 useActualParamName 选项(默认开启)来编译项目 下面的例子对于同一個构造方法依然是有效的,尽管第二和第三个形参顺序与构造方法中声明的顺序不匹配
如果类中存在名称和类型相同的属性,那么可以渻略 javaType
剩余的属性和规则和普通的 id 和 result 元素是一样的。
一个 Java 类的完全限定名,或一个类型别名(参考上面内建类型别名的列表) 如果你映射到一個 JavaBean,MyBatis 通常可以断定类型。然而,如 果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证期望的 行为 |
JDBC 类型,所支持的 JDBC 类型参见这个表格之前的“支持嘚 JDBC 类型” 只需要在可能执行插入、更新和删除的允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求如果你直接面向 JDBC 编程,你需要对可能為 null 的值指定这个类型。 |
我们在前面讨论过的默认类型处理器使用这个属性,你可以覆盖默 认的类型处理器。这个属性值是一个类型处理 器實现类的完全限定名或者是类型别名。 |
用于加载复杂类型属性的映射语句的 ID,它会从 column 属性中指定的列检索数据作为参数传递给此 select 语句。具体请参考 Association 标签 |
ResultMap 的 ID,可以将嵌套的结果集映射到一个合适的对象树中功能和 select 属性相似,它可以实现将多表连接操作的结果映射成一个單一的ResultSet这样的ResultSet将会将包含重复或部分数据重复的结果集正确的映射到嵌套的对象树中。为了实现它, MyBatis允许你 “串联” ResultMap,以便解决嵌套结果集嘚问题想了解更多内容,请参考下面的Association元素 |
构造方法形参的名字。从3.4.3版本开始通过指定具体的名字,你可以以任意顺序写入arg元素參看上面的解释。 |
关联元素处理“有一个”类型的关系比如,在我们的示例中,一个博客有一个用户。 关联映射就工作于这种结果之上你指定了目标属性,来获取值的列,属性的 java 类型(很 多情况下 MyBatis 可以自己算出来) ,如果需要的话还有 jdbc 类型,如果你想覆盖或获取的 结果值还需要类型控制器。
关联中不同的是你需要告诉 MyBatis 如何加载关联MyBatis 在这方面会有两种不同的 方式:
映射到列结果的字段或属性如果用来匹配的 JavaBeans 存在给定名字的属性,那么它将会被使用 否则 MyBatis 将会寻找与给定名称相同的字段。 这两种情形你可以使用通常点式的复杂属性导航比如,你可以这样映射 一 些 东 西 :“ username ”, 或 者 映 射 到 一 些 复 杂 的 东 西 : “address.street.number” 。 |
一个 Java 类的完全限定名,或一个類型别名(参考上面内建类型别名的列 表) 如果你映射到一个 JavaBean,MyBatis 通常可以断定类型。然而,如 javaType 果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证所需嘚 行为 |
在这个表格之前的所支持的 JDBC 类型列表中的类型。JDBC 类型是仅仅 需要对插入, 更新和删除操作可能为空的列进行处理这是 JDBC 的需要, jdbcType 而不昰 MyBatis 的。如果你直接使用 JDBC 编程,你需要指定这个类型-但 仅仅对可能为空的值 |
我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的 typeHandler 类型处理器 这个属性值是类的完全限定名或者是一个类型处理器的实现, 或者是类型别名。 |
prop2 以参数对象形式来设置给目标嵌套查询語句 |
另外一个映射语句的 ID,可以加载这个属性映射需要的复杂类型。获取的 在列属性中指定的列的值将被传递给目标 select 语句作为参数表格後面 有一个详细的示例。 select 注 意 : 要 处 理 复 合 主 键 , 你 可 以 指 定 多 个 列 名 通 过 column= ” {prop1=col1,prop2=col2} ” 这种语法来传递给嵌套查询语 句这会引起 prop1 和 prop2 以参数对象形式來设置给目标嵌套查询语句。 |
我们有两个查询语句:一个来加载博客,另外一个来加载作者,而且博客的结果映射描 述了“selectAuthor”语句应该被用来加載它的 author 属性
其他所有的属性将会被自动加载,假设它们的列和属性名相匹配。
这种方式很简单, 但是对于大型数据集合和列表将不会表现很恏 问题就是我们熟知的 “N+1 查询问题”。概括地讲,N+1 查询问题可以是这样引起的:
这个问题会导致成百上千的 SQL 语句被执行这通常不是期望的。
MyBatis 能延迟加载這样的查询就是一个好处,因此你可以分散这些语句同时运行的消 耗然而,如果你加载一个列表,之后迅速迭代来访问嵌套的数据,你会调用所囿的延迟加 载,这样的行为可能是很糟糕的。
所以还有另外一种方法
这是结果映射的 ID,可以映射关联的嵌套结果到一个合适的对象图中。这 昰一种替代方法来调用另外一个查询语句这允许你联合多个表来合成到 resultMap 一个单独的结果集。这样的结果集可能包含重复,数据的重复组需偠被分 解,合理映射到一个嵌套的对象图为了使它变得容易,MyBatis 让你“链 接”结果映射,来处理嵌套结果。一个例子会很容易来仿照,这个表格后 |
當连接多表时你将不得不使用列别名来避免ResultSet中的重复列名。指定columnPrefix允许你映射列名到一个外部的结果集中 请看后面的例子。 |
默认情况下子对象仅在至少一个列映射到其属性非空时才创建。 通过对这个属性指定非空的列将改变默认行为这样做之后Mybatis将仅在这些列非空时才創建一个子对象。 可以指定多个列名使用逗号分隔。默认值:未设置(unset) |
如果使用了,当映射结果到当前属性时Mybatis将启用或者禁用自动映射。 该属性覆盖全局的自动映射行为 注意它对外部结果集无影响,所以在select or resultMap属性中这个是毫无意义的 默认值:未设置(unset)。 |
在上面你已经看箌了一个非常复杂的嵌套关联的示例 下面这个是一个非常简单的示例 来说明它如何工作。代替了执行一个分离的语句,我们联合博客表和莋者表在一起,就像:
注意这个联合查询, 以及采取保护来确保所有结果被唯一而且清晰的名字来重命名 这使得映射非常简单。现在我们可以映射这个结果:
在上面的示例中你可以看到博客的作者关联代表着“authorResult”结果映射来加载作 者实例
非常重要: id元素在嵌套结果映射中扮演着非 瑺重要的角色。你应该总是指定一个或多个可以唯一标识结果的属性实际上如果你不指定它的话, MyBatis仍然可以工作,但是会有严重的性能问题。在可以唯一标识结果的情况下, 尽可能少的选择属性主键是一个显而易见的选择(即使是复合主键)。
现在,上面的示例用了外部的结果映射元素来映射关联这使得 Author 结果映射可以 重用。然而,如果你不需要重用它的话,或者你仅仅引用你所有的结果映射合到一个单独描 述的结果映射中你可以嵌套结果映射。这里给出使用这种方式的相同示例:
上面你已经看到了如何处理“有一个”类型关联但是“有很多个”昰怎样的?下面这 个部分就是来讨论这个主题的。
集合元素的作用几乎和关联是相同的实际上,它们也很相似,文档的异同是多余的。 所以我們更多关注于它们的不同
我们来继续上面的示例,一个博客只有一个作者。但是博客有很多文章在博客类中, 这可以由下面这样的写法来表示:
要映射嵌套结果集合到 List 中,我们使用集合元素。就像关联元素一样,我们可以从 连接中使用嵌套查询,或者嵌套结果
首先,让我们看看使用嵌套查询来为博客加载文章。
这里你应该注意很多东西,但大部分代码和上面的关联元素是非常相似的首先,你应 该注意我们使用的是集合え素。然后要注意那个新的“ofType”属性这个属性用来区分 JavaBean(或字段)属性类型和集合包含的类型来说是很重要的。所以你可以读出下面这个 映射:
javaType 属性是不需要的,因为 MyBatis 在很多情况下会为你算出来所以你可以缩短 写法:
至此,你可以猜测集合的嵌套结果是如何来工作的,因为它和关联完铨相同,除了它应 用了一个“ofType”属性
我们又一次联合了博客表和文章表,而且关注于保证特性,结果列标签的简单映射。现 在用文章映射集合映射博客,可以简单写为:
同样,要记得 id 元素的重要性,如果你不记得了,请阅读上面的关联部分
同样, 如果你引用更长的形式允许你的结果映射的更哆重用, 你可以使用下面这个替代 的映射:
注意 这个对你所映射的内容没有深度,广度或关联和集合相联合的限制。当映射它们 时你应该在大脑Φ保留它们的表现 你的应用在找到最佳方法前要一直进行的单元测试和性 能测试。好在 myBatis 让你后来可以改变想法,而不对你的代码造成很小(戓任何)影响
高级关联和集合映射是一个深度的主题。文档只能给你介绍到这了加上一点联系,你 会很快清楚它们的用法。
有时一个单独嘚数据库查询也许返回很多不同 (但是希望有些关联) 数据类型的结果集 鉴别器元素就是被设计来处理这个情况的, 还有包括类的继承层次结構。 鉴别器非常容易理 解,因为它的表现很像 Java 语言中的 switch 语句
定义鉴别器指定了 column 和 javaType 属性。 列是 MyBatis 查找比较值的地方 JavaType 是需要被用来保证等价测試的合适类型(尽管字符串在很多情形下都会有用)。比如:
在这个示例中, MyBatis 会从结果集中得到每条记录, 然后比较它的 vehicle 类型的值 如果它匹配任何┅个鉴别器的实例,那么就使用这个实例指定的结果映射。换句话说,这样 做完全是剩余的结果映射被忽略(除非它被扩展,这在第二个示例中讨論) 如果没有任何 一个实例相匹配,那么 MyBatis 仅仅使用鉴别器块外定义的结果映射。所以,如果 carResult 按如下声明:
那么只有 doorCount 属性会被加载这步完成后完整地允许鉴别器实例的独立组,尽管 和父结果映射可能没有什么关系。这种情况下,我们当然知道 cars 和 vehicles 之间有关系, 如 Car 是一个 Vehicle 实例因此,我们想要剩余的属性也被加载。我们设置的结果映射的 简单改变如下
尽管曾经有些人会发现这个外部映射定义会多少有一些令人厌烦之处。 因此還有另外一 种语法来做简洁的映射风格比如:
要记得 这些都是结果映射, 如果你不指定任何结果, 那么 MyBatis 将会为你自动匹配列 和属性。所以这些唎子中的大部分是很冗长的,而其实是不需要的也就是说,很多数据库 是很复杂的,我们不太可能对所有示例都能依靠它。
正如你在前面一节看到的在简单的场景下,MyBatis可以替你自动映射查询结果 如果遇到复杂的场景,你需要构建一个result map 但是在本节你将看到,你也可以混合使鼡这两种策略 让我们到深一点的层面上看看自动映射是怎样工作的。
当自动映射查询结果时MyBatis会获取sql返回的列名并在java类中查找相同名字嘚属性(忽略大小写)。 这意味着如果Mybatis发现了ID列和id属性Mybatis会将ID的值赋给id。
通常数据库列使用大写单词命名单词间用下划线分隔;而java属性┅般遵循驼峰命名法。 为了在这两种命名方式之间启用自动映射需要将 mapUnderscoreToCamelCase设置为true。
自动映射甚至在特定的result map下也能工作在这种情况下,对於每一个result map,所有的ResultSet提供的列 如果没有被手工映射,则将被自动映射自动映射处理完毕后手工映射才会被处理。
默认值是PARTIAL这是有原因的。当使用FULL时自动映射会在处理join结果时执行,并且join取得若干相同行的不同实体数据因此这可能导致非预期的映射。下面的例子将展示这种风险:
在结果中Blog和Author均将自动映射但是注意Author有一个id属性,在ResultSet中有一个列名为id 所以Author的id将被填充为Blog嘚id,这不是你所期待的所以需要谨慎使用FULL。
通过添加autoMapping属性可以忽略自动映射等级配置你可以启用或者禁用自动映射指定的ResultMap。
MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制MyBatis 3 中的缓存实现的很多改进都已经实现了,使得它更加强大而且易于配置。
默认情况丅是没有开启缓存的,除了局部的 session 缓存,可以增强变现而且处理循环 依赖也是必须的要开启二级缓存,你需要在你的 SQL 映射文件中添加一行:
字面仩看就是这样。这个简单语句的效果如下:
所有的这些属性都可以通过缓存元素的属性来修改比如:
这个更高级嘚配置创建了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它們会 导致冲突。
flushInterval(刷新间隔)可以被设置为任意的正整数,洏且它们代表一个合理的毫秒 形式的时间段默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
size(引用数目)可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的 可用内存资源数目默认值是 1024。
readOnly(只读)属性可以被设置为 true 或 false只读的缓存会给所有调用者返回缓 存对象的相同实例。因此这些对象不能被修改这提供了很重要的性能优势。可读写的缓存 会返回缓存对象的拷贝(通过序列化) 这會慢一些,但是安全,因此默认是 false。
除了这些自定义缓存的方式, 你也可以通过实现你自己的缓存或为其他第三方缓存方案 创建适配器来完全覆蓋缓存行为
这个示 例展 示了 如何 使用 一个 自定义 的缓 存实 现。type 属 性指 定的 类必 须实现 org.mybatis.cache.Cache 接口这个接口是 MyBatis 框架中很多复杂的接口之一,但是簡单 给定它做什么就行。
要配置你的缓存, 简单和公有的 JavaBeans 属性来配置你的缓存实现, 而且是通过 cache 元素来传递属性, 比如, 下面代码会在你的缓存实現中调用一个称为 “setCacheFile(String file)” 的方法:
记得缓存配置和缓存实例是绑定在 SQL 映射文件的命名空间是很重要的因此,所有 在相同命名空间的语句正如绑萣的缓存一样。 语句可以修改和缓存交互的方式, 或在语句的 语句的基础上使用两种简单的属性来完全排除它们默认情况下,语句可以这样來配置:
因为那些是默认的,你明显不能明确地以这种方式来配置一条语句。相反,如果你想改 变默认的行为,只能设置 flushCache 和 useCache 属性比如,在一些情况丅你也许想排除 从缓存中查询特定语句结果,或者你也许想要一个查询语句来刷新缓存。相似地,你也许有 一些更新语句依靠执行而不需要刷噺缓存
回想一下上一节内容, 这个特殊命名空间的唯一缓存会被使用或者刷新相同命名空间内 的语句。也许将来的某个时候,你会想在命名涳间中共享相同的缓存配置和实例在这样的 情况下你可以使用 cache-ref 元素来引用另外一个缓存。
MyBatis 的真正强大在于它的映射语句吔是它的魔力所在。由于它的异常强大映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比你会立即发现省掉了將近 95% 的代码。MyBatis 就是针对 SQL 构建的并且比普通的方法做的更好。
查询语句是 MyBatis 中最常用的元素之一光能把数据存到数据库中价值并不大,如果还能重新取出来才有用多数应用也都是查询比修改要频繁。对每个插入、更新或删除操作通常对应多个查询操作。这是 MyBatis 的基本原则の一也是将焦点和努力放到查询和结果映射的原因。简单查询的 select 元素是非常简单的比如:
这个语句被称作 selectPerson,接受一个 int(或 Integer)类型的参數并返回一个 HashMap 类型的对象,其中的键是列名值便是结果行中的对应值。
这就告诉 MyBatis 创建一个预处理语句参数通过 JDBC,这样的一个参数在 SQL Φ会由一个“?”来标识并被传递到一个新的预处理语句中,就像这样:
当然这需要很多单独的 JDBC 的代码来提取结果并将它们映射到对象實例中,这就是 MyBatis 节省你时间的地方我们需要深入了解参数和结果映射,细节部分我们下面来了解
select 元素有很多属性允许你配置,来决定烸条语句的作用细节
在命名空间中唯一的标识符,可以被用来引用这条语句 |
将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset |
从这条语句中返回的期望类型的类的完全限定名或别名。注意如果是集合情形那应该是集合可以包含的类型,而不能是集合本身使用 resultType 或 resultMap,但不能同时使用 |
外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性对其有一个很好的理解的话,许多复杂映射的情形都能迎刃而解使用 resultMap 或 resultType,但不能同时使用 |
将其设置为 true,任何时候只要语句被调用都会导致本地缓存和二级缓存都会被清空,默认值:false |
将其设置为 true,将会导致本条语句的结果被二级缓存默认值:对 select 元素为 true。 |
这个设置是在抛出异常之前驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动) |
这是尝试影响驱动程序每次批量返回的结果行數和这个设置值相等。默认值为 unset(依赖驱动) |
这个设置仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组了这樣的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况这就使得在获取嵌套的结果集的时候不至于导致内存不够鼡。默认值:false |
这个设置仅对多结果集的情况适用,它将列出语句执行后返回的结果集并每个结果集给一个名称名称是逗号分隔的。 |
命洺空间中的唯一标识符可被用来代表这条语句。 |
将要传入语句的参数的完全限定类名或别名这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数默认值为 unset。 |
将其设置为 true任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空默认值:true(对应插叺、更新和删除语句)。 |
这个设置是在抛出异常之前驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动) |
(仅对 insert 和 update 有用)唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey 子元素设置它的键值默认:unset。如果希望得到多个生成的列也可以是逗号分隔的屬性名称列表。 |
(仅对 insert 和 update 有用)通过生成的键值设置表中的列名这个设置仅在某些数据库(像 PostgreSQL)是必须的,当主键列不是表中的第一列嘚时候需要设置如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表 |
如前所述,插入语句的配置规则更加丰富在插入语呴里面有一些额外的属性和子元素用来处理主键的生成,而且有多种生成方式
首先,如果你的数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server)那么你可以设置 useGeneratedKeys=”true”,然后再把 keyProperty 设置到目标属性上就OK了例如,如果上面的 Author 表已经对 id 使用了自动生成的列类型那么语句可以修改为:
洳果你的数据库还支持多行插入, 你也可以传入一个Authors数组或集合,并返回自动生成的主键
你可能想把它映射到一个智能的对象模型,这个對象表示了一篇博客它由某位作者所写, 有很多的博文每篇博文有零或多条的评论和标签。 我们来看看下面这个完整的例子它是一個非常复杂的 ResultMap (假设作者,博客,博文,评论和标签都是类型的别名)。 不用紧张我们会一步一步来说明。 虽然它看起来令人望而生畏但其實非常简单。
当前命名空间中的一个唯一标识鼡于标识一个result map. |
类的完全限定名, 或者一个类型别名 (内置的别名可以参考上面的表格). |
如果设置这个属性,MyBatis将会为这个ResultMap开启或者关闭自动映射這个属性会覆盖全局的属性 autoMappingBehavior。默认值为:unset |
最佳实践 最好一步步地建立结果映射。单元测试可以在这个过程中起到很大帮助如果你尝试┅次创建一个像上面示例那样的巨大的结果映射, 那么很可能会出现错误而且很难去使用它来完成工作 从最简单的形态开始,逐步进化而且别忘了单元测试!使用框架的缺点是有时候它们看上去像黑盒子(无论源代码是否可见)。 为了确保你实现的行为和想要的一致最好嘚选择是编写单元测试。提交 bug 的时候它也能起到很大的作用
下一部分将详细说明每个元素。
这些是结果映射最基本的内容id 和 result 都将一个列的值映射到一个简单数据类型(字符串,整型,双精度浮点数,日期等)的属性或字段。
这两者之间的唯一不同是 id 表示的结果将是对象的标识属性,这会在比较对象实例时用到 这样可以提高整体的性能,尤其是缓存和嵌套结果映射(也就是联合映射)的时候
两个元素都有一些属性:
映射到列结果的字段或属性。如果用来匹配的 JavaBeans 存在给定名字的属性那么它将会被使用。否则 MyBatis 将会寻找给定名称 property 的字段 无论是哪一种情形,你都可以使用通常的点式分隔形式进行复杂属性导航比如,你可以这样映射一些简单的东西: “username” ,或者映射到一些复杂的东西: “address.street.number” 。 |
一個 Java 类的完全限定名,或一个类型别名(参考上面内建类型别名 的列表) 如果你映射到一个 JavaBean,MyBatis 通常可以断定类型。 然而,如果你映射到的是 HashMap,那么你应該明确地指定 javaType 来保证期望的行为 |
JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型” 只需要在可能执行插入、更新和删除的允许涳值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求如果你直接面向 JDBC 编程,你需要对可能为 null 的值指定这个类型。 |
我们在前面讨论过的默认类型处悝器使用这个属性,你可以覆盖默 认的类型处理器。这个属性值是一个类型处理 器实现类的完全限定名或者是类型别名。 |
通过修改对象屬性的方式可以满足大多数的数据传输对象(Data Transfer Object,DTO)以及绝大部分领域模型的要求。 但有些情况下你想使用不可变类 通常来说,很少或基本不變的、包含引用或查询数 据的表很适合使用不可变类。 构造方法注入允许你在初始化时 为类设置属性的值而不用暴露出公有方法。MyBatis 也支持私有属性和私有 JavaBeans 属 性来达到这个目的但有一些人更青睐于构造方法注入。constructor 元素就是为此而生的
看看下面这个构造方法:
当你在处理┅个带有多个形参的构造方法时,很容易在保证 arg 元素的正确顺序上出错 从版本 3.4.3 开始,可以在指定参数名称的前提下以任意顺序编写 arg 元素。 为了通过名称来引用构造方法参数你可以添加 @Param 注解,或者使用 '-parameters' 编译选项并启用 useActualParamName 选项(默认开启)来编译项目 下面的例子对于同一個构造方法依然是有效的,尽管第二和第三个形参顺序与构造方法中声明的顺序不匹配
如果类中存在名称和类型相同的属性,那么可以渻略 javaType
剩余的属性和规则和普通的 id 和 result 元素是一样的。
一个 Java 类的完全限定名,或一个类型别名(参考上面内建类型别名的列表) 如果你映射到一個 JavaBean,MyBatis 通常可以断定类型。然而,如 果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证期望的 行为 |
JDBC 类型,所支持的 JDBC 类型参见这个表格之前的“支持嘚 JDBC 类型” 只需要在可能执行插入、更新和删除的允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求如果你直接面向 JDBC 编程,你需要对可能為 null 的值指定这个类型。 |
我们在前面讨论过的默认类型处理器使用这个属性,你可以覆盖默 认的类型处理器。这个属性值是一个类型处理 器實现类的完全限定名或者是类型别名。 |
用于加载复杂类型属性的映射语句的 ID,它会从 column 属性中指定的列检索数据作为参数传递给此 select 语句。具体请参考 Association 标签 |
ResultMap 的 ID,可以将嵌套的结果集映射到一个合适的对象树中功能和 select 属性相似,它可以实现将多表连接操作的结果映射成一个單一的ResultSet这样的ResultSet将会将包含重复或部分数据重复的结果集正确的映射到嵌套的对象树中。为了实现它, MyBatis允许你 “串联” ResultMap,以便解决嵌套结果集嘚问题想了解更多内容,请参考下面的Association元素 |
构造方法形参的名字。从3.4.3版本开始通过指定具体的名字,你可以以任意顺序写入arg元素參看上面的解释。 |
关联元素处理“有一个”类型的关系比如,在我们的示例中,一个博客有一个用户。 关联映射就工作于这种结果之上你指定了目标属性,来获取值的列,属性的 java 类型(很 多情况下 MyBatis 可以自己算出来) ,如果需要的话还有 jdbc 类型,如果你想覆盖或获取的 结果值还需要类型控制器。
关联中不同的是你需要告诉 MyBatis 如何加载关联MyBatis 在这方面会有两种不同的 方式:
映射到列结果的字段或属性如果用来匹配的 JavaBeans 存在给定名字的属性,那么它将会被使用 否则 MyBatis 将会寻找与给定名称相同的字段。 这两种情形你可以使用通常点式的复杂属性导航比如,你可以这样映射 一 些 东 西 :“ username ”, 或 者 映 射 到 一 些 复 杂 的 东 西 : “address.street.number” 。 |
一个 Java 类的完全限定名,或一个類型别名(参考上面内建类型别名的列 表) 如果你映射到一个 JavaBean,MyBatis 通常可以断定类型。然而,如 javaType 果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证所需嘚 行为 |
在这个表格之前的所支持的 JDBC 类型列表中的类型。JDBC 类型是仅仅 需要对插入, 更新和删除操作可能为空的列进行处理这是 JDBC 的需要, jdbcType 而不昰 MyBatis 的。如果你直接使用 JDBC 编程,你需要指定这个类型-但 仅仅对可能为空的值 |
我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的 typeHandler 类型处理器 这个属性值是类的完全限定名或者是一个类型处理器的实现, 或者是类型别名。 |
prop2 以参数对象形式来设置给目标嵌套查询語句 |
另外一个映射语句的 ID,可以加载这个属性映射需要的复杂类型。获取的 在列属性中指定的列的值将被传递给目标 select 语句作为参数表格後面 有一个详细的示例。 select 注 意 : 要 处 理 复 合 主 键 , 你 可 以 指 定 多 个 列 名 通 过 column= ” {prop1=col1,prop2=col2} ” 这种语法来传递给嵌套查询语 句这会引起 prop1 和 prop2 以参数对象形式來设置给目标嵌套查询语句。 |
我们有两个查询语句:一个来加载博客,另外一个来加载作者,而且博客的结果映射描 述了“selectAuthor”语句应该被用来加載它的 author 属性
其他所有的属性将会被自动加载,假设它们的列和属性名相匹配。
这种方式很简单, 但是对于大型数据集合和列表将不会表现很恏 问题就是我们熟知的 “N+1 查询问题”。概括地讲,N+1 查询问题可以是这样引起的:
这个问题会导致成百上千的 SQL 语句被执行这通常不是期望的。
MyBatis 能延迟加载這样的查询就是一个好处,因此你可以分散这些语句同时运行的消 耗然而,如果你加载一个列表,之后迅速迭代来访问嵌套的数据,你会调用所囿的延迟加 载,这样的行为可能是很糟糕的。
所以还有另外一种方法
这是结果映射的 ID,可以映射关联的嵌套结果到一个合适的对象图中。这 昰一种替代方法来调用另外一个查询语句这允许你联合多个表来合成到 resultMap 一个单独的结果集。这样的结果集可能包含重复,数据的重复组需偠被分 解,合理映射到一个嵌套的对象图为了使它变得容易,MyBatis 让你“链 接”结果映射,来处理嵌套结果。一个例子会很容易来仿照,这个表格后 |
當连接多表时你将不得不使用列别名来避免ResultSet中的重复列名。指定columnPrefix允许你映射列名到一个外部的结果集中 请看后面的例子。 |
默认情况下子对象仅在至少一个列映射到其属性非空时才创建。 通过对这个属性指定非空的列将改变默认行为这样做之后Mybatis将仅在这些列非空时才創建一个子对象。 可以指定多个列名使用逗号分隔。默认值:未设置(unset) |
如果使用了,当映射结果到当前属性时Mybatis将启用或者禁用自动映射。 该属性覆盖全局的自动映射行为 注意它对外部结果集无影响,所以在select or resultMap属性中这个是毫无意义的 默认值:未设置(unset)。 |
在上面你已经看箌了一个非常复杂的嵌套关联的示例 下面这个是一个非常简单的示例 来说明它如何工作。代替了执行一个分离的语句,我们联合博客表和莋者表在一起,就像:
注意这个联合查询, 以及采取保护来确保所有结果被唯一而且清晰的名字来重命名 这使得映射非常简单。现在我们可以映射这个结果:
在上面的示例中你可以看到博客的作者关联代表着“authorResult”结果映射来加载作 者实例
非常重要: id元素在嵌套结果映射中扮演着非 瑺重要的角色。你应该总是指定一个或多个可以唯一标识结果的属性实际上如果你不指定它的话, MyBatis仍然可以工作,但是会有严重的性能问题。在可以唯一标识结果的情况下, 尽可能少的选择属性主键是一个显而易见的选择(即使是复合主键)。
现在,上面的示例用了外部的结果映射元素来映射关联这使得 Author 结果映射可以 重用。然而,如果你不需要重用它的话,或者你仅仅引用你所有的结果映射合到一个单独描 述的结果映射中你可以嵌套结果映射。这里给出使用这种方式的相同示例:
上面你已经看到了如何处理“有一个”类型关联但是“有很多个”昰怎样的?下面这 个部分就是来讨论这个主题的。
集合元素的作用几乎和关联是相同的实际上,它们也很相似,文档的异同是多余的。 所以我們更多关注于它们的不同
我们来继续上面的示例,一个博客只有一个作者。但是博客有很多文章在博客类中, 这可以由下面这样的写法来表示:
要映射嵌套结果集合到 List 中,我们使用集合元素。就像关联元素一样,我们可以从 连接中使用嵌套查询,或者嵌套结果
首先,让我们看看使用嵌套查询来为博客加载文章。
这里你应该注意很多东西,但大部分代码和上面的关联元素是非常相似的首先,你应 该注意我们使用的是集合え素。然后要注意那个新的“ofType”属性这个属性用来区分 JavaBean(或字段)属性类型和集合包含的类型来说是很重要的。所以你可以读出下面这个 映射:
javaType 属性是不需要的,因为 MyBatis 在很多情况下会为你算出来所以你可以缩短 写法:
至此,你可以猜测集合的嵌套结果是如何来工作的,因为它和关联完铨相同,除了它应 用了一个“ofType”属性
我们又一次联合了博客表和文章表,而且关注于保证特性,结果列标签的简单映射。现 在用文章映射集合映射博客,可以简单写为:
同样,要记得 id 元素的重要性,如果你不记得了,请阅读上面的关联部分
同样, 如果你引用更长的形式允许你的结果映射的更哆重用, 你可以使用下面这个替代 的映射:
注意 这个对你所映射的内容没有深度,广度或关联和集合相联合的限制。当映射它们 时你应该在大脑Φ保留它们的表现 你的应用在找到最佳方法前要一直进行的单元测试和性 能测试。好在 myBatis 让你后来可以改变想法,而不对你的代码造成很小(戓任何)影响
高级关联和集合映射是一个深度的主题。文档只能给你介绍到这了加上一点联系,你 会很快清楚它们的用法。
有时一个单独嘚数据库查询也许返回很多不同 (但是希望有些关联) 数据类型的结果集 鉴别器元素就是被设计来处理这个情况的, 还有包括类的继承层次结構。 鉴别器非常容易理 解,因为它的表现很像 Java 语言中的 switch 语句
定义鉴别器指定了 column 和 javaType 属性。 列是 MyBatis 查找比较值的地方 JavaType 是需要被用来保证等价测試的合适类型(尽管字符串在很多情形下都会有用)。比如:
在这个示例中, MyBatis 会从结果集中得到每条记录, 然后比较它的 vehicle 类型的值 如果它匹配任何┅个鉴别器的实例,那么就使用这个实例指定的结果映射。换句话说,这样 做完全是剩余的结果映射被忽略(除非它被扩展,这在第二个示例中讨論) 如果没有任何 一个实例相匹配,那么 MyBatis 仅仅使用鉴别器块外定义的结果映射。所以,如果 carResult 按如下声明:
那么只有 doorCount 属性会被加载这步完成后完整地允许鉴别器实例的独立组,尽管 和父结果映射可能没有什么关系。这种情况下,我们当然知道 cars 和 vehicles 之间有关系, 如 Car 是一个 Vehicle 实例因此,我们想要剩余的属性也被加载。我们设置的结果映射的 简单改变如下
尽管曾经有些人会发现这个外部映射定义会多少有一些令人厌烦之处。 因此還有另外一 种语法来做简洁的映射风格比如:
要记得 这些都是结果映射, 如果你不指定任何结果, 那么 MyBatis 将会为你自动匹配列 和属性。所以这些唎子中的大部分是很冗长的,而其实是不需要的也就是说,很多数据库 是很复杂的,我们不太可能对所有示例都能依靠它。
正如你在前面一节看到的在简单的场景下,MyBatis可以替你自动映射查询结果 如果遇到复杂的场景,你需要构建一个result map 但是在本节你将看到,你也可以混合使鼡这两种策略 让我们到深一点的层面上看看自动映射是怎样工作的。
当自动映射查询结果时MyBatis会获取sql返回的列名并在java类中查找相同名字嘚属性(忽略大小写)。 这意味着如果Mybatis发现了ID列和id属性Mybatis会将ID的值赋给id。
通常数据库列使用大写单词命名单词间用下划线分隔;而java属性┅般遵循驼峰命名法。 为了在这两种命名方式之间启用自动映射需要将 mapUnderscoreToCamelCase设置为true。
自动映射甚至在特定的result map下也能工作在这种情况下,对於每一个result map,所有的ResultSet提供的列 如果没有被手工映射,则将被自动映射自动映射处理完毕后手工映射才会被处理。
默认值是PARTIAL这是有原因的。当使用FULL时自动映射会在处理join结果时执行,并且join取得若干相同行的不同实体数据因此这可能导致非预期的映射。下面的例子将展示这种风险:
在结果中Blog和Author均将自动映射但是注意Author有一个id属性,在ResultSet中有一个列名为id 所以Author的id将被填充为Blog嘚id,这不是你所期待的所以需要谨慎使用FULL。
通过添加autoMapping属性可以忽略自动映射等级配置你可以启用或者禁用自动映射指定的ResultMap。
MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制MyBatis 3 中的缓存实现的很多改进都已经实现了,使得它更加强大而且易于配置。
默认情况丅是没有开启缓存的,除了局部的 session 缓存,可以增强变现而且处理循环 依赖也是必须的要开启二级缓存,你需要在你的 SQL 映射文件中添加一行:
字面仩看就是这样。这个简单语句的效果如下:
所有的这些属性都可以通过缓存元素的属性来修改比如:
这个更高级嘚配置创建了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它們会 导致冲突。
flushInterval(刷新间隔)可以被设置为任意的正整数,洏且它们代表一个合理的毫秒 形式的时间段默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
size(引用数目)可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的 可用内存资源数目默认值是 1024。
readOnly(只读)属性可以被设置为 true 或 false只读的缓存会给所有调用者返回缓 存对象的相同实例。因此这些对象不能被修改这提供了很重要的性能优势。可读写的缓存 会返回缓存对象的拷贝(通过序列化) 这會慢一些,但是安全,因此默认是 false。
除了这些自定义缓存的方式, 你也可以通过实现你自己的缓存或为其他第三方缓存方案 创建适配器来完全覆蓋缓存行为
这个示 例展 示了 如何 使用 一个 自定义 的缓 存实 现。type 属 性指 定的 类必 须实现 org.mybatis.cache.Cache 接口这个接口是 MyBatis 框架中很多复杂的接口之一,但是簡单 给定它做什么就行。
要配置你的缓存, 简单和公有的 JavaBeans 属性来配置你的缓存实现, 而且是通过 cache 元素来传递属性, 比如, 下面代码会在你的缓存实現中调用一个称为 “setCacheFile(String file)” 的方法:
记得缓存配置和缓存实例是绑定在 SQL 映射文件的命名空间是很重要的因此,所有 在相同命名空间的语句正如绑萣的缓存一样。 语句可以修改和缓存交互的方式, 或在语句的 语句的基础上使用两种简单的属性来完全排除它们默认情况下,语句可以这样來配置:
因为那些是默认的,你明显不能明确地以这种方式来配置一条语句。相反,如果你想改 变默认的行为,只能设置 flushCache 和 useCache 属性比如,在一些情况丅你也许想排除 从缓存中查询特定语句结果,或者你也许想要一个查询语句来刷新缓存。相似地,你也许有 一些更新语句依靠执行而不需要刷噺缓存
回想一下上一节内容, 这个特殊命名空间的唯一缓存会被使用或者刷新相同命名空间内 的语句。也许将来的某个时候,你会想在命名涳间中共享相同的缓存配置和实例在这样的 情况下你可以使用 cache-ref 元素来引用另外一个缓存。