Mybatis作为一个ORM框架,也对SQL的高级查询做了支持,下面我们学习Mybatis下的一对一、一对多、多对多的查询。
此案例的业务关系是用户、订单、订单详情、商品之间的关系,其中,
一个订单只能属于一个人。
一个订单可以有多个订单详情。
一个订单详情中包含一个商品信息。
它们的关系是:
订单和人是 一对一的关系。
订单和订单详情是 一对多 的关系。
订单和商品是 多对多的关系。
数据库关系图:
一对一查询
需求:一对一查询:查询订单,并且查询出下单人的信息。SQL: SELECT o.*, u.user_name,u. NAME FROM tb_order o LEFT JOIN tb_user u ON o.user_id = u.id WHERE order_number = '20140921001'
第一种实现:
User.java
package cn.zto.mybatis.pojo;
import java.util.Date;
/**
*
* @ClassName: User
* @Description:对应用户表
* @author: xyc
* @date: 2017年2月11日 下午8:11:13
*
*/
public class User implements java.io.Serializable{
private static final long serialVersionUID = 1L;
private Long id;
// 用户名
private String userName;
// 密码
private String password;
// 姓名
private String name;
// 年龄
private Integer age;
// 性别,1男性,2女性
private Integer sex;
// 出生日期
private Date birthday;
// 创建时间
private Date created;
// 更新时间
private Date updated;
//getter And setter
@Override
public String toString() {
return "User [id=" + id + ", userName=" + userName + ", password=" + password + ", name=" + name
+ ", age=" + age + ", sex=" + sex + ", birthday=" + birthday + ", created=" + created
+ ", updated=" + updated + "]";
}
}
Order.java
package cn.zto.mybatis.pojo;
/**
*
* @ClassName: Order
* @Description:对应订单表
* @author: xyc
* @date: 2017年2月11日 下午8:22:24
*
*/
public class Order {
private Integer id;
private Long userId;
private String orderNumber;
//getter And setter
@Override
public String toString() {
return "Order [id=" + id + ", userId=" + userId + ", orderNumber=" + orderNumber + "]";
}
}
OrderUser.java
package cn.zto.mybatis.pojo;
/**
*
* @ClassName: OrderUser
* @Description: 此类用于补字段
* @author: xyc
* @date: 2017年2月11日 下午8:24:51
*
*/
public class OrderUser extends Order{
private String userName;
private String name;
//getter and setter
@Override
public String toString() {
return "OrderUser [userName=" + userName + ", name=" + name + "]";
}
}
OrderMapper.java
package cn.zto.mybatis.mapper;
import cn.zto.mybatis.pojo.OrderUser;
/**
* 测试高级查询
*/
public interface OrderMapper {
/**
* 查询订单,并且查询出下单人的信息。
* @param orderNumber
* @return
*/
public OrderUser queryOrderAndUserByOrderNumber(String orderNumber);
}
OrderMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.zto.mybatis.mapper.OrderMapper">
<select id="queryOrderAndUserByOrderNumber" resultType="OrderUser">
SELECT
o.*,
u.user_name,
u.name
FROM
tb_order o
LEFT JOIN tb_user u ON o.user_id = u.id
WHERE order_number = #{orderNumber}
</select>
</mapper>
记得加入到全局配置文件中:
测试结果:
第二种实现:
核心思想:面向对象的思想,在Order对象中添加User对象。
package cn.zto.mybatis.pojo;
/**
*
* @ClassName: Order
* @Description:对应订单表
* @author: xyc
* @date: 2017年2月11日 下午8:33:12
*
*/
public class Order {
private Integer id;
private Long userId;
private String orderNumber;
private User user;
//getter And setter
}
Mapper接口:
/**
* 查询订单,并且查询出下单人的信息。
* @param orderNumber
* @return
*/
public Order queryOrderAndUserByOrderNumber2(String orderNumber);
Mapper映射文件
<!-- 在resultMap中,完成对象映射时,autoMapping默认为false,因此这里需要打开,否则有的属性关系无法对应 --> <resultMap type="Order" id="orderResultMap" autoMapping="true"> <id column="id" property="id"/> <!--
association:用户映射java对象
javaType:对象中的属性的类型 --><association property="user" javaType="User" autoMapping="true"><id column="user_id" property="id"/></association></resultMap>property: 对象中的属性名称
<!-- 使用resultType不能完成自动映射,所以需要手动完成结果集的映射,需要使用resultMap实现。-->
<select id="queryOrderAndUserByOrderNumber2" resultMap="orderResultMap">SELECTo.*,u.user_name, u.nameFROMtb_order oLEFT JOIN tb_user u ON o.user_id = u.idWHERE order_number = #{orderNumber}</select> 测试:
@Test
public void testQueryOrderAndUserByOrderNumber2() {
Order order = this.orderMapper.queryOrderAndUserByOrderNumber2("20140921001");
System.out.println(order);
}
结果:
一对多查询
SELECT
o.*,
u.user_name,
u.name,
od.id detail_id,
od.item_id,
od.total_price
FROM
tb_order o
LEFT JOIN tb_user u ON o.user_id = u.id
LEFT JOIN tb_orderdetail od ON o.id = od.order_id
WHERE order_number = '20140921001'
package cn.zto.mybatis.pojo;
import java.util.List;
/**
* 订单表
*
*/
public class Order {
private Integer id;
private Long userId;
private String orderNumber;
private User user;
//映射一对多查询的订单详情
private List<Orderdetail> orderdetails;
//getter And setter
@Override
public String toString() {
return "Order [id=" + id + ", userId=" + userId + ", orderNumber=" + orderNumber + ", user=" + user
+ ", orderdetails=" + orderdetails + "]";
}
}
package cn.zto.mybatis.pojo;
/**
*
* @ClassName: Orderdetail
* @Description:订单详情
* @author: xyc
* @date: 2017年2月11日 下午9:42:28
*
*/
public class Orderdetail {
private Integer id;
private Double totalPrice;
private Integer status;
private Integer itemId;
private Item item;
//getter and setter
}
接口:
/**
* 查询订单,查询出下单人信息并且查询出订单详情。
*
* @param orderNumber
* @return
*/
public Order queryOrderAndUserAndOrderDetailByOrderNumber(String orderNumber);
<resultMap type="Order" id="orderUserOrderDetailresultMap" autoMapping="true">
<id column="id" property="id"/>
<!--
association:映射java对象
property: 对象中的属性名称
javaType:对象中的属性的类型
-->
<association property="user" javaType="User" autoMapping="true">
<id column="user_id" property="id"/>
</association>
<!--
collection:映射集合
javaType: 属性的java类型
ofType:集合中的对象的java类型
-->
<collection property="orderdetails" javaType="List" ofType="Orderdetail" autoMapping="true">
<id column="detail_id" property="id"/>
</collection>
</resultMap>
<select id="queryOrderAndUserAndOrderDetailByOrderNumber" resultMap="orderUserOrderDetailresultMap">
SELECT
o.*,
u.user_name,
u.name,
od.id detail_id,
od.item_id,
od.total_price
FROM
tb_order o
LEFT JOIN tb_user u ON o.user_id = u.id
LEFT JOIN tb_orderdetail od ON o.id = od.order_id
WHERE order_number = #{orderNumber}
</select>
测试:
@Test
public void testQueryOrderAndUserAndOrderDetailByOrderNumber() {
Order order = this.orderMapper.queryOrderAndUserAndOrderDetailByOrderNumber("20140921001");
System.out.println(order);
}
结果:
多对多查询
SELECT
o.*,
u.user_name,
u.name,
od.id detail_id,
od.item_id,
od.total_price,
i.item_name,
i.item_price,
i.item_detail
FROM
tb_order o
LEFT JOIN tb_user u ON o.user_id = u.id
LEFT JOIN tb_orderdetail od ON o.id = od.order_id
LEFT JOIN tb_item i ON od.item_id = i.id
WHERE order_number = '20140921001'
Item.java
package cn.zto.mybatis.pojo;
/**
* 商品表
*/
public class Item {
private Integer id;
private String itemName;
private Float itemPrice;
private String itemDetail;
//getter and setter
}
Orderdetail.java
package cn.zto.mybatis.pojo;
/**
*
* @ClassName: Orderdetail
* @Description:订单详情
* @author: xyc
* @date: 2017年2月11日 下午9:42:28
*
*/
public class Orderdetail {
private Integer id;
private Double totalPrice;
private Integer status;
private Integer itemId;
private Item item;
//gette and setter
}
接口:
/**
* 多对多查询
* 查询订单,查询出下单人信息并且查询出订单详情中的商品数据。
* @param orderNumber
* @return
*/
public Order queryOrderAndUserAndOrderDetailAndItemByOrderNumber(String orderNumber);
<resultMap type="Order" id="orderUserOrderDetailItemResultMap" autoMapping="true">
<id column="id" property="id"/>
<!--
property: 对象中的属性名称
javaType:对象中的属性的类型
-->
<association property="user" javaType="User" autoMapping="true">
<id column="user_id" property="id"/>
</association>
<!-- 映射orderdetails -->
<collection property="orderdetails" javaType="List" ofType="Orderdetail" autoMapping="true">
<id property="id" column="detail_id"/>
<!--
映射Orderdetail中的Item对象
-->
<association property="item" javaType="Item" autoMapping="true">
<id property="id" column="item_id"/>
</association>
</collection>
</resultMap>
<select id="queryOrderAndUserAndOrderDetailAndItemByOrderNumber" resultMap="orderUserOrderDetailItemResultMap">
SELECT
o.*,
u.user_name,
u.name,
od.id detail_id,
od.item_id,
od.total_price,
i.item_name,
i.item_price,
i.item_detail
FROM
tb_order o
LEFT JOIN tb_user u ON o.user_id = u.id
LEFT JOIN tb_orderdetail od ON o.id = od.order_id
LEFT JOIN tb_item i ON od.item_id = i.id
WHERE order_number = #{orderNumber}
</select>
测试:
@Test
public void testQueryOrderAndUserAndOrderDetailAndItemByOrderNumber() {
Order order = this.orderMapper.queryOrderAndUserAndOrderDetailAndItemByOrderNumber("20140921001");
System.out.println(order);
}
结果:
ResultMap的继承
<!-- 在resultMap中,完成对象映射时,autoMapping默认为false,因此这里需要打开,否则有的属性关系无法对应 -->
<resultMap type="Order" id="orderResultMap" autoMapping="true">
<id column="id" property="id"/>
<!--
property: 对象中的属性名称
javaType:对象中的属性的类型
-->
<association property="user" javaType="User" autoMapping="true">
<id column="user_id" property="id"/>
</association>
</resultMap>
我们可以通过继承来完善
<resultMap type="Order" id="orderUserOrderDetailItemResultMap" extends="orderResultMap" autoMapping="true">
<!-- 映射orderdetails -->
<collection property="orderdetails" javaType="List" ofType="Orderdetail" autoMapping="true">
<id property="id" column="detail_id"/>
<!--
映射Orderdetail中的Item对象
-->
<association property="item" javaType="Item" autoMapping="true">
<id property="id" column="item_id"/>
</association>
</collection>
</resultMap>
extends的值为上面resultMap的id.
延迟加载
延迟加载的意义在于,虽然是关联查询,但不是及时将关联的数据查询出来,而且在需要的时候进行查询。
开启延迟加载:
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
lazyLoadingEnabled:true使用延迟加载,false禁用延迟加载。默认为true
aggressiveLazyLoading:true启用时,当延迟加载开启时访问对象中一个懒对象属性时,将完全加载这个对象的所有懒对象属性。false,当延迟加载时,按需加载对象属性(即访问对象中一个懒对象属性,不会加载对象中其他的懒对象属性)。默认为true
①. 添加cglib支持
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
②.全局配置文件中开启延迟加载
<settings>
<!-- 开启驼峰映射 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 二级缓存的全局开关 -->
<setting name="cacheEnabled" value="true"/>
<!-- 延迟加载的开关 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!--
true启用时,当延迟加载开启时访问对象中一个懒对象属性时,将完全加载这个对象的所有懒对象属性。
false,当延迟加载时,按需加载对象属性(即访问对象中一个懒对象属性,不会加载对象中其他的懒对象属性)
-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
③.编写接口:
/**
* 测试延迟加载
* 查询订单,并且查询出下单人的信息。
* @param orderNumber
* @return
*/
public Order lazyQueryOrderAndUserByOrderNumber(String orderNumber);
④.编写Mapper.xml
⑤.测试:
@Test
public void testLazyQueryOrderAndUserByOrderNumber() {
Order order = this.orderMapper.lazyQueryOrderAndUserByOrderNumber("20140921001");
User user = order.getUser();//触发延迟加载
System.out.println(user);
System.out.println(order);
}
结果: