MyBatis的建立基于这样一个思想:数据库并非您想怎样就怎样的。虽然咱们但愿全部的数据库遵照第三范式或BCNF(修正的第三范式),但它们不是。若是有一个数据库可以完美映射到全部应用程序,也将是很是棒的,但也没有。结果集映射就是MyBatis为解决这些问题而提供的解决方案。例如,咱们如何映射下面这条语句? [html] view plaincopy <select id="selectBlog_by_id" parameterType="int" resultMap="Blog_result"> select b.id, b.title, b.author_id, a.id, a.username, a.password, a.email, a.bio from Blog b left join Author a on b.author_id = a.id where b.id = #{id} </select> [html] view plaincopy <resultMap type="Blog" id="Blog_result"> <id column="id" property="id" /> <result column="title" property="title"/> <!-- 映射关联的对象 --> <association property="author" javaType="Author"> <id column="author_id" property="id"/> <result column="username" property="username"/> <result column="password" property="password"/> <result column="email" property="email"/> <result column="bio" property="bio"/> </association> </resultMap> resultMap resultMap属性:type为java实体类;id为此resultMap的标识。 resultMap能够设置的映射: 1. constructor – 用来将结果反射给一个实例化好的类的构造器 a) idArg – ID 参数;将结果集标记为ID,以方便全局调用 b) arg –反射到构造器的一般结果 2. id – ID 结果,将结果集标记为ID,以方便全局调用 3. result – 反射到JavaBean 属性的普通结果 4. association – 复杂类型的结合;多个结果合成的类型 a) nested result mappings – 几resultMap 自身嵌套关联,也能够引用到一个其它上 5. collection –复杂类型集合a collection of complex types 6. nested result mappings – resultMap 的集合,也能够引用到一个其它上 7. discriminator – 使用一个结果值以决定使用哪一个resultMap a) case – 基本一些值的结果映射的case 情形 i. nested result mappings –一个case 情形自己就是一个结果映射,所以也能够包括一些相同的元素,也能够引用一个外部resultMap。 è最佳实践:逐步地生成resultMap,单元测试对此很是有帮助。若是您尝试一会儿就生成像上面这样巨大的resultMap,可能会出错,而且工做起来很是吃力。从简单地开始,再一步步地扩展,而且进行单元测试。使用框架开发有一个缺点,它们有时像是一个黑合。为了确保达到您所预想的行为,最好的方式就是进行单元测试。这对提交bugs也很是有用。 下面一部分将详细说明每一个元素。 id、result id、result是最简单的映射,id为主键映射;result其余基本数据库表字段到实体类属性的映射。 [html] view plaincopy <resultMap type="Blog" id="Blog_result"> <id column="id" property="id" /> <result column="title" property="title"/> </resultMap> 这些是结果映射最基本内容。id和result都映射一个单独列的值到简单数据类型(字符串,整型,双精度浮点数,日期等)的单独属性或字段。 这二者之间的惟一不一样是id表示的结果将是当比较对象实例时用到的标识属性。这帮助来改进总体表现,特别是缓存和嵌入结果映射(也就是联合映射)。 id、result语句属性配置细节: 属性 描述 property 映射到列结果的字段或属性。若是匹配的是存在的,和给定名称相同的JavaBeans的属性,那么就会使用。 不然MyBatis将会寻找给定名称的字段。这两种情形你可使用一般点式的复杂属性导航。 好比,你能够这样映射一些东西:“username”,或者映射到一些复杂的东西:“address.street.number”。 column 从数据库中获得的列名,或者是列名的重命名标签。 这也是一般和会传递给resultSet.getString(columnName)方法参数中相同的字符串。 javaType 一个Java类的彻底限定名,或一个类型别名(参加上面内建类型别名的列表)。 若是你映射到一个JavaBean,MyBatis一般能够判定类型。 然而,若是你映射到的是HashMap,那么你应该明确地指定javaType来保证所需的行为。 jdbcType 在这个表格以后的所支持的JDBC类型列表中的类型。JDBC类型是仅仅须要对插入,更新和删除操做可能为空的列进行处理。 这是JDBC的须要,而不是MyBatis的。若是你直接使用JDBC编程,你须要指定这个类型-但仅仅对可能为空的值。 typeHandler 咱们在前面讨论过默认的类型处理器。使用这个属性,你能够覆盖默认的类型处理器。 这个属性值是类的彻底限定名或者是一个类型处理器的实现,或者是类型别名。 支持的JDBC类型 MyBatis支持以下的JDBC类型: BIT FLOAT CHAR TIMESTAMP OTHER UNDEFINED TINYINT REAL VARCHAR BINARY BLOB NVARCHAR SMALLINT DOUBLE LONGVARCHAR VARBINARY CLOB NCHAR INTEGER NUMERIC DATE LONGVARBINARY BOOLEAN NCLOB BIGINT DECIMAL TIME NULL CURSOR constructor <构造方法> [html] view plaincopy <resultMap type="Blog" id="Blog_result_cs"> <constructor> <idArg column="id" javaType="int"/> <arg column="title" javaType="String"/> </constructor> </resultMap> 对于大多数数据传输对象(Data Transfer Object,DTO)类型,属性能够起做用,并且像绝大多数的领域模型,指令也许是你想使用一成不变的类的地方。一般包含引用或查询数据的表不多或基本不变的话对一成不变的类来讲是合适的。构造方法注入容许你在初始化时为类设置属性的值,而不用暴露出公有方法。MyBatis也支持私有属性和私有JavaBeans属性来达到这个目的,可是一些人更青睐构造方法注入。Constructor(构造方法)元素支持这个。 [java] view plaincopy package com.accp.mybatis.model; public class Blog { private Integer id; private String title; private Integer authorId; public Blog() { } public Blog(Integer id, String title, Integer authorId) { super(); System.out.println("使用有参Blog"); this.id = id; this.title = title; this.authorId = authorId; } //........ } 为了向这个构造方法中注入结果,MyBatis须要经过它的参数的类型来标识构造方法。Java没有自查(或反射)参数名的方法。因此当建立一个构造方法元素时,保证参数是按顺序排列的,并且数据类型也是肯定的。 association<关联映射> [html] view plaincopy <!-- 映射关联的对象 --> <association property="author" javaType="Author"> <id column="author_id" property="id"/> <result column="username" property="username"/> <result column="password" property="password"/> <result column="email" property="email"/> <result column="bio" property="bio"/> </association> 关联元素处理“有一个”类型的关系。好比,在咱们的示例中,一个博客有一个用户。关联映射就工做于这种结果之上。你指定了目标属性,来获取值的列,属性的java类型(不少状况下MyBatis能够本身算出来),若是须要的话还有jdbc类型,若是你想覆盖或获取的结果值还须要类型控制器。 关联中不一样的是你须要告诉MyBatis如何加载关联。MyBatis在这方面会有两种不一样的方式: (1) 嵌套查询:经过执行另一个SQL映射语句来返回预期的复杂类型。 (2) 嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集。 首先,然让咱们来查看这个元素的属性。 属性 描述 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 咱们在前面讨论过默认的类型处理器。使用这个属性,你能够覆盖默认的类型处理器。这个属性值是类的彻底限定名或者是一个类型处理器的实现,或者是类型别名。 联合嵌套选择(Nested Select for Association) select 经过这个属性,经过ID引用另外一个加载复杂类型的映射语句。从指定列属性中返回的值,将做为参数设置给目标select 语句。表格下方将有一个例子。注意:在处理组合键时,您可使用column=”{prop1=col1,prop2=col2}”这样的语法,设置多个列名传入到嵌套语句。这就会把prop1和prop2设置到目标嵌套语句的参数对象中。 SELECT<联合查询> [html] view plaincopy <resultMap type="Blog" id="Blog_result"> <association property="author" column="author_id" javaType="Author" select="selectAuthorById" /> </resultMap> <select id="selectAuthorById" parameterType="int" resultType="Author"> select * from Author where id = #{id} </select> <!-- select关联,建议在一对一的状况下使用。 在此处,若是selectBlogById返回多个Blog,将会带来N+1问题 --> <select id="selectBlogById" parameterType="int" resultMap="Blog_result"> select * from Blog where id = #{id} </select> 咱们有两个查询语句:一个来加载博客,另一个来加载做者,并且博客的结果映射描述了“selectAuthorById”语句应该被用来加载它的author属性。 其余全部的属性将会被自动加载,假设它们的列和属性名相匹配。 这种方式很简单,可是对于大型数据集合和列表将不会表现很好。问题就是咱们熟知的“N+1查询问题”。归纳地讲,N+1查询问题能够是这样引发的: (1)你执行了一个单独的SQL语句来获取结果列表(就是“+1”)。 (2)对返回的每条记录,你执行了一个查询语句来为每一个加载细节(就是“N”)。 关联的嵌套结果 resultMap 这是结果映射的ID,能够映射关联的嵌套结果到一个合适的对象图中。这是一种替代方法来调用另一个查询语句。这容许你联合多个表来合成到一个单独的结果集。这样的结果集可能包含重复,数据的重复组须要被分解,合理映射到一个嵌套的对象图。为了使它变得容易,MyBatis让你“连接”结果映射,来处理嵌套结果。例子会很容易来仿照,这个表格后面也有一个示例。 [html] view plaincopy <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> 注意这个联合查询,以及采起保护来确保全部结果被惟一并且清晰的名字来重命名。这使得映射很是简单。如今咱们能够映射这个结果: [html] view plaincopy <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元素扮演了很是重要的角色。应该一般指定一个或多个属性,它们能够用来惟一标识结果。实际上就是若是你不使用它(id元素),可是会产生一个严重的性能问题,不过MyBatis仍然能够正常工做。选择的属性越少越好,它们能够惟一地标识结果。主键就是一个显而易见的选择(即使是联合主键)。 如今,上面的示例用了外部的结果映射元素来映射关联。这使得Author结果映射能够重用。然而,若是你不须要重用它的话,或者你仅仅引用你全部的结果映射合到一个单独描述的结果映射中。你能够嵌套结果映射。这里给出使用这种方式的相同示例: [html] view plaincopy <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> collection<集合> [html] view plaincopy <collection property="posts" ofType="Post"> <id property="id" column="id"/> <result property="subject" column="subject"/> <result property="body" column="body"/> </collection> 集合元素的做用几乎和关联是相同的。实际上,它们也很类似。 咱们来继续上面的示例,一个博客只有一个做者。可是博客有不少文章。在博客类中,这能够由下面这样的写法来表示: private List<Post> posts; 要映射嵌套结果集合到List中,咱们使用集合元素。就像关联元素同样,咱们能够从链接中使用嵌套查询,或者嵌套结果。 集合的嵌套查询 首先,让咱们看看使用嵌套查询来为博客加载文章。 [html] view plaincopy <resultMap type="Blog" id="Blog_result"> <association property="author" column="author_id" javaType="Author" select="selectAuthorById" /> <collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectBlogPost" /> </resultMap> <select id="selectBlogPost" resultType="Post" parameterType="int"> select * from Post where blog_id=#{id} </select> <select id="selectAuthorById" parameterType="int" resultType="Author"> select * from Author where id = #{id} </select> <!-- select关联,建议在一对一的状况下使用。 在此处,若是selectBlogById返回多个Blog,将会带来N+1问题 --> <select id="selectBlogById" parameterType="int" resultMap="Blog_result"> select * from Blog where id = #{id} </select> 首先,你应该注意咱们使用的是集合元素。而后要注意那个新的“ofType”属性。这个属性用来区分JavaBean(或字段)属性类型和集合包含的类型来讲是很重要的。因此你能够读出下面这个映射: [html] view plaincopy <collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectBlogPost" /> 读做:“在Post类型的ArrayList中的posts的集合。” 集合的嵌套结果 [html] view plaincopy <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> 如今用文章映射集合映射博客,能够简单写为: [html] view plaincopy <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元素的重要性。 若是你引用更长的形式容许你的结果映射的更多重用,你可使用下面这个替代的映 [html] view plaincopy <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> discriminator鉴别器 [html] view plaincopy <discriminator javaType="int" column="draft"> <case value="1" resultType="DraftPost"/> </discriminator> 有时一个单独的数据库查询也许返回不少不一样(可是但愿有些关联)数据类型的结果集。鉴别器元素就是被设计来处理这个状况的,还有包括类的继承层次结构。鉴别器很是容易理解,由于它的表现很像Java语言中的switch语句。 定义鉴别器指定了column和javaType属性。列是MyBatis查找比较值的地方。JavaType是须要被用来保证等价测试的合适类型(尽管字符串在不少情形下都会有用)。好比: [html] view plaincopy <resultMap id="vehicleResult" type="Vehicle"> <id property=”id” column="id" /> <result property="vin" column="vin"/> <result property="year" column="year"/> <result property="make" column="make"/> <result property="model" column="model"/> <result property="color" column="color"/> <discriminator javaType="int" column="vehicle_type"> <case value="1" resultMap="carResult"/> <case value="2" resultMap="truckResult"/> <case value="3" resultMap="vanResult"/> <case value="4" resultMap="suvResult"/> </discriminator> </resultMap> <resultMap id="carResult" type="Car"> <result property=”doorCount” column="door_count" /> </resultMap> 上面示例中,MyBatis会从结果集中获得每条记录,而后比较它的vehicle类型的值。若是它匹配任何一个鉴别器的实例,那么就使用这个实例指定的结果映射。 还有另一种语法来作简洁的映射风格。好比: [html] view plaincopy <resultMap id="vehicleResult" type="Vehicle"> <id property=”id” column="id" /> <result property="vin" column="vin"/> <result property="year" column="year"/> <result property="make" column="make"/> <result property="model" column="model"/> <result property="color" column="color"/> <discriminator javaType="int" column="vehicle_type"> <case value="1" resultType="carResult"> <result property=”doorCount” column="door_count" /> </case> <case value="2" resultType="truckResult"> <result property=”boxSize” column="box_size" /> <result property=”extendedCab” column="extended_cab" /> </case> <case value="3" resultType="vanResult"> <result property=”powerSlidingDoor” column="power_sliding_door" /> </case> <case value="4" resultType="suvResult"> <result property=”allWheelDrive” column="all_wheel_drive" /> </case> </discriminator> </resultMap>