三、MyBatis系列:Mapper 映射 之 多级关联查询结果映射
现在有这么几张表,员工表(user)、订单(orders)、订单明细(orderdetails)、产品信息(items)它们的物理模型图如下:
希望能查询出某些用户下的订单详细信息,而且希望能一条SQL查出所有相关的数据,并映射到POJO实体中。
返回的结果应该包含用户信息、订单及明细、相应产品信息。
数据映射为实体结构如下:
1 package cn.xleos.mybatis.po; 2 3 public class User { 4 private int id; 5 private String username; 6 private Date birthday; 7 private String sex; 8 private String address; 9 private List<Order> orders; // 一个用户关联多个订单 10 } 11 public class Order { 12 private int id; 13 private User user; // 订单所属的用户. 14 private int number; 15 private Date createtime; 16 private List<OrderDetail> details; // 一个订单关联多个订单明细 17 } 18 public class OrderDetail { 19 private int id; 20 private Order order; // 明细所属的订单 21 private Item item; // 每个订单明细关联一个产品信息. 22 private int number; 23 } 24 public class Item { 25 private int id; 26 private String name; 27 private Float price; 28 private String detail; 29 }
现在来看看这个mapper的映射配置如何编写,注意示例中的以下几点:
1、constructor 实体的构造方法
2、autoMapping 自动属性映射
3、collection 集合属性的映射
4、association 关联属性的映射
5、mapUnderscoreToCamelCase 是否开启自动驼峰命名规则,全局配置.
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.3//EN" 3 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 4 <mapper namespace="test"> 5 <!-- 定义一个返回结果映射,告诉 MyBatis你希望的映射方式. 6 1. type 是你希望结果映射到的POJO类型; 7 2. autoMapping 默认为 false; 8 --> 9 <resultMap type="cn.xleos.mybatis.po.User" id="userOrderDetailMap" autoMapping="true"> 10 <!-- 当你希望以自定构造方法来创建实例时,就需要该配置节,如:User(Integer id, String username). 11 正如期望的那样,为构造方法传递参数值,是按照配置节顺序传入的. 12 >idArg: 传入一个做为主键值的参数. 13 >arg: 传入一个普通的参数值. 14 --> 15 <constructor> 16 <!-- 使用 User(Integer id)构造函数来传入 id 参数. 17 >这里有个地方要注意,无论 javaType 设置的值为 int 或 integer 都是 java.lang.Integer 的别名. 18 >这样在 User 类型中就不能使用 int 做为 id 的类型, 否则会找不到该构造方法. 19 >若希望在 User 类中使用 int 做为构造方法类型 User(int id),那这里的 javaType 应设置为 _int,它才是 int 类型别名. 20 >还需要注意的是: 其它类型也可能存在相同的问题. 21 --> 22 <idArg column="user_id" javaType="int" /> 23 <arg column="username" javaType="String" /> 24 </constructor> 25 26 <!-- 当构造方法中没有定义一个主键做为参数,那么你可能需要使用以下的方式配置主键属性. --> 27 <!-- <id property="id" column="user_id" /> --> 28 29 <!-- 默认情况下只有配置了 result 才会将数据绑定至该属性. 30 >只有 autoMapping 设置为 true 时, 即使不配置 sex/address,也会自动绑定属性.会忽略大小写的. 31 >还有一种情况是:数据字段命名时常常将多个字段名使用下划线间隔,如:user_name 32 >如果希望能正确的映射到 username 属性中,需要在全局配置文件中设置: mapUnderscoreToCamelCase=true 33 --> 34 <result property="birthday" column="birthday" /> 35 <!-- 36 <result column="sex" property="sex" /> 37 <result column="address" property="address" /> 38 --> 39 40 <!-- collection 表示你将要映射一个 List<E> 类型的属性, 这里映射到的另一个resultMap; --> 41 <collection property="orders" resultMap="orderDetailMap" /> 42 </resultMap> 43 <!-- 表单及明细的结果映射 --> 44 <resultMap type="cn.xleos.mybatis.po.Order" id="orderDetailMap"> 45 <id property="id" column="orders_id" /> 46 <result column="createtime" property="createtime" /> 47 <result column="number" property="number" /> 48 <!-- 表单所属用户的关联属性映射 --> 49 <association property="user" resultMap="userOrderDetailMap" /> 50 <!-- 表单明细集合的结果映射 --> 51 <collection property="details" ofType="cn.xleos.mybatis.po.OrderDetail"> 52 <id property="id" column="detail_id" /> 53 <result property="number" column="items_num" /> 54 <!-- 明细所属表单的关联属性映射 --> 55 <association property="order" column="orders_id" resultMap="orderDetailMap" /> 56 <!-- 明细所属产品的关联属性映射 --> 57 <association property="item" javaType="cn.xleos.mybatis.po.Item"> 58 <id property="id" column="items_id" /> 59 <result property="name" column="name" /> 60 <result property="price" column="price" /> 61 <result property="detail" column="detail" /> 62 </association> 63 </collection> 64 </resultMap> 65 66 <!-- 您必须定义 resultMap,否则将无法得到你预期的结果. --> 67 <select id="getUserOrderDetails" resultMap="userOrderDetailMap"> 68 SELECT 69 `user`.id AS user_id, 70 `user`.username AS user_name, 71 `user`.sex AS sex, 72 `user`.birthday AS birthday, 73 `user`.address AS address, 74 orders.id AS orders_id, 75 orders.createtime, 76 orders.number, 77 orderdetail.id AS detail_id, 78 orderdetail.items_num, 79 items.id AS items_id, 80 items.`name`, 81 items.price, 82 items.detail 83 FROM orderdetail 84 INNER JOIN orders ON orders.id = orderdetail.orders_id 85 INNER JOIN `user` ON `user`.id = orders.user_id 86 INNER JOIN items ON items.id = orderdetail.items_id 87 </select> 88 </mapper>
数据查询结果与实体映射的关系,通过下图可以很容易理解。
- A 数据源会去重,生成一个 user 对象
- B 数据源会去重,生成 user 对象中的 orders 集合中的记录。
- 在生成 order 对象的 user 属性时,会从一级缓存获取 user 对象,它的 key 就是 user_id
- C 数据源会去重,生成 order 对象中的 details 集中中的记录;
- D 数据源会生成 detail 对象的 item 关联属性;