在实现xml映射器的时候,或许我们并没有显式地去使用ResultMap,但实际上我们已经用到了ResultMap。ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。简而言之ResluMap实际上就是一个用户自定义的返回集(类似于视图)。
目录
ResultMap和ResultType(ResultMap的简单映射)
ResultMap实现高级结果映射
首先ResultMap 元素有很多子元素和一个值得深入探讨的结构。
下面是resultMap 元素的概念视图。
ResultMap 的属性列表
自定义结果映射(id,result关键字)
Id 和 Result 的属性
嵌套结果映射
association (一对一映射)
association 属性
association 实例:尽量避免对象属性重名,会出bug
collection (一对多映射)
collection 实例:尽量避免对象属性重名,会出bug
ResultMap和ResultType(ResultMap的简单映射)
通常情况下简单的xml映射器是这样
这里注意到我们使用的是resultType,在这些情况下,MyBatis 会在幕后自动创建一个 ResultMap,再根据属性名来映射列到 JavaBean 的属性上。
也就是ResultMap作为将 查询结果id,username,hashedPassword映射到User对象的中间的容器。所以如果你不想去统一数据库表中字段名和java bean 属性名,你完全可以使用ResultMap来自定义数据库表中字段名和java bean 属性名的映射。
以上面的例子举例:
在这个例子里,我将原java bean中的id属性改为id1,而我在ResultMap中自定义映射,从而实现对ResultType的取代。
ResultMap实现高级结果映射
这里的高级结果映射体现在数据库外键链接上。譬如一个Author有多篇blog,那么我如何通过Mybatis查询,使我能够查询到特定的一位Author所写的Blog呢?这里就需要ResultMap了,通过前面的介绍我们知道ResultMap可以是的程序员能够自定义字段到java bean属性的映射。
同样ResultMap也能做到对其他表的关联映射。通过对其他表的关联映射从而实现级联查询。在JDBC中我们会使用笛卡尔积连接,自然连接,或者外连接来实现,但是在Mybatis中我们可以化简这个过程。
首先ResultMap 元素有很多子元素和一个值得深入探讨的结构。
下面是resultMap 元素的概念视图。
结果映射(resultMap)
- constructor - 用于在实例化类时,注入结果到构造方法中
- idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
- arg - 将被注入到构造方法的一个普通结果
- id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
- result – 注入到字段或 JavaBean 属性的普通结果
- association – 一个复杂类型的关联;许多结果将包装成这种类型
- 嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用
- collection – 一个复杂类型的集合
- 嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用
- discriminator – 使用结果值来决定使用哪个 resultMap
- case – 基于某些值的结果映射
- 嵌套结果映射 – case 也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射
ResultMap 的属性列表
属性 描述 id 当前命名空间中的一个唯一标识,用于标识一个结果映射。(一个标识符,跟以前的博客解释的一样) type 类的完全限定名, 或者一个类型别名。(你想要自定义从类属性映射到字段名的类的完全限定名或者别名) autoMapping 如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。默认值:未设置(unset)。
自定义结果映射(id,result关键字)
自定义从类属性映射到字段名,需要用到 id,result 关键字。
这些元素是结果映射的基础。id 和 result 元素都将一个列的值映射到一个简单数据类型(String, int, double, Date 等)的属性或字段。
这两者之间的唯一不同是,id 元素对应的属性会被标记为对象的标识符,在比较对象实例时使用。 这样可以提高整体的性能,尤其是进行缓存和嵌套结果映射(也就是连接映射)的时候。
id,result 两个元素都有一些属性:
Id 和 Result 的属性
属性 描述
property 映射到列结果的字段或属性。(实际上就是java bean 的属性名)如果 JavaBean 有这个名字的属性(property),会先使用该属性。否则 MyBatis 将会寻找给定名称的字段(field)。 无论是哪一种情形,你都可以使用常见的点式分隔形式进行复杂属性导航。 比如,你可以这样映射一些简单的东西:“username”,或者映射到一些复杂的东西上:“address.street.number”。
column 数据库中的列名,或者是列的别名。(数据库表中字段名)一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。 javaType 一个 Java 类的全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。
jdbcType JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可以为空值的列指定这个类型。 typeHandler 我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的类型处理器。 这个属性值是一个类型处理器实现类的全限定名,或者是类型别名。
嵌套结果映射
association (一对一映射)
关联(association)元素处理“有一个”类型的关系。 比如,在我们的示例中,一个博客有一个用户。关联结果映射和其它类型的映射工作方式差不多。
你需要告诉 MyBatis 如何加载关联。MyBatis 有两种不同的方式加载关联:
- 嵌套 Select 查询:通过执行另外一个 SQL 映射语句来加载期望的复杂类型。
- 嵌套结果映射:使用嵌套的结果映射来处理连接结果的重复子集。
association 属性
property | 映射到列结果的字段或属性(映射关联java bean的属性名,注意这里的属性是ResultMap所声明的Type中的java bean的属性而不是后面javaType对应的java bean的属性)。如果用来匹配的 JavaBean 存在给定名字的属性,那么它将会被使用。否则 MyBatis 将会寻找给定名称的字段。 无论是哪一种情形,你都可以使用通常的点式分隔形式进行复杂属性导航。 比如,你可以这样映射一些简单的东西:“username”,或者映射到一些复杂的东西上:“address.street.number”。 |
javaType | 一个 Java 类的完全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。(映射关联java bean的完全限定名或别名) 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。 |
jdbcType | JDBC 类型,所支持的 JDBC 类型参见这个表格之前的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可能存在空值的列指定这个类型。 |
typeHandler | 我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的类型处理器。 这个属性值是一个类型处理器实现类的完全限定名,或者是类型别名。 |
关联的嵌套 Select 查询
column | 数据库中的列名,或者是列的别名。(数据库字段名)一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。 注意:在使用复合主键的时候,你可以使用 column="{prop1=col1,prop2=col2}" 这样的语法来指定多个传递给嵌套 Select 查询语句的列名。这会使得 prop1 和 prop2 作为参数对象,被设置为对应嵌套 Select 语句的参数。 |
select | 用于加载复杂类型属性的映射语句的 ID,它会从 column 属性指定的列中检索数据,作为参数传递给目标 select 语句。 具体请参考下面的例子。注意:在使用复合主键的时候,你可以使用 column="{prop1=col1,prop2=col2}" 这样的语法来指定多个传递给嵌套 Select 查询语句的列名。这会使得 prop1 和 prop2 作为参数对象,被设置为对应嵌套 Select 语句的参数。 |
fetchType | 可选的。有效值为 lazy 和 eager。 指定属性后,将在映射中忽略全局配置参数 lazyLoadingEnabled,使用属性的值。 |
association 实例:尽量避免对象属性重名,会出bug
场景说明,现有两张表,User(id,name,seat_id),Seat(seat_id,position).每一个User都有一个位子,现在嵌套查询id为1的User的位子的信息。
关联的嵌套结果映射
resultMap | 结果映射的 ID,可以将此关联的嵌套结果集映射到一个合适的对象树中。 它可以作为使用额外 select 语句的替代方案。它可以将多表连接操作的结果映射成一个单一的 ResultSet。这样的 ResultSet 有部分数据是重复的。 为了将结果集正确地映射到嵌套的对象树中, MyBatis 允许你“串联”结果映射,以便解决嵌套结果集的问题。使用嵌套结果映射的一个例子在表格以后。 |
columnPrefix | 当连接多个表时,你可能会不得不使用列别名来避免在 ResultSet 中产生重复的列名。指定 columnPrefix 列名前缀允许你将带有这些前缀的列映射到一个外部的结果映射中。 详细说明请参考后面的例子。 |
notNullColumn | 默认情况下,在至少一个被映射到属性的列不为空时,子对象才会被创建。 你可以在这个属性上指定非空的列来改变默认行为,指定后,Mybatis 将只在这些列非空时才创建一个子对象。可以使用逗号分隔来指定多个列。默认值:未设置(unset)。 |
autoMapping | 如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。注意,本属性对外部的结果映射无效,所以不能搭配 select 或 resultMap 元素使用。默认值:未设置(unset)。 |
下面的例子则是一个非常简单的例子,用于演示嵌套结果映射如何工作。 现在我们将User表和seat表连接在一起,而不是执行一个独立的查询语句。
也就是先连接两张表再定义ResultMap返回结果,比第一种方式简单多了。值得注意的是之前谈到association中有ResultMap属性,也就是你可以把association内部的东西单独写到另一个ResultMap中去。
collection (一对多映射)
collection实现嵌套查询与association 相差无几,值得注意的是属性ofType替换了JavaType,并且返回的属性property对应的实体对象的属性应该是List类型。
collection 实例:尽量避免对象属性重名,会出bug
场景说明,现有两张表,User(id,name),Seat(seatid,position,userid).一个User都有同很多位子,现在嵌套查询id为1的User的位子信息。
方法一:
方法二:
个人推荐方法二,易懂一些。