1、association属性(关联的嵌套映射)
第一个属性是“关联的嵌套映射”,该属性的标签名称为“association”。在resultMap中,当映射type为Java包装类时,可能会遇到包装类中含有其他Java包装类的属性,这里resultMap提供了association标签来定义结果集中包含的其他结果集。
【示例】查询订单信息(Order),并获取关联的用户信息(User)。
(1)在数据库中创建用户信息表(tb_user)和订单信息表(tb_order)。
-- 创建“用户信息”数据表
CREATE TABLE IF NOT EXISTS tb_user
(
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '用户编号',
user_name VARCHAR(50) NOT NULL COMMENT '用户姓名',
blog_url VARCHAR(50) NOT NULL COMMENT '博客地址',
remark VARCHAR(50) COMMENT '备注',
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间'
) COMMENT = '用户信息表';
-- 添加数据
INSERT INTO tb_user(user_name,blog_url,remark) VALUES('pan_junbiao的博客','https://blog.youkuaiyun.com/pan_junbiao','您好,欢迎访问 pan_junbiao的博客');
-- 创建“订单信息”数据表
CREATE TABLE IF NOT EXISTS tb_order
(
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '订单编号',
order_no VARCHAR(50) NOT NULL COMMENT '订单单号',
product_count INT DEFAULT '0' COMMENT '商品数量',
price_total DECIMAL(9,2) DEFAULT '0' COMMENT '商品总价',
user_id INT NOT NULL COMMENT '订单用户',
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '下单时间'
) COMMENT = '订单信息表';
-- 添加数据
INSERT INTO tb_order(order_no,product_count,price_total,user_id) VALUE('T20191209001',5,14295,1)
(2)创建用户信息持久化类(User.java)和订单信息持久化类(Order.java)。
package com.pjb.mybatis.po;
import java.io.Serializable;
import java.util.Date;
/**
* 用户信息持久化类
* @author pan_junbiao
**/
public class User implements Serializable
{
private int id; //用户ID
private String userName; //用户姓名
private String blogUrl; //博客地址
private String remark; //备注
private Date createTime; //创建时间
//省略getter与setter方法...
}
package com.pjb.mybatis.po;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 订单信息持久化类
* @author pan_junbiao
**/
public class Order implements Serializable
{
public int id; //订单ID
public String orderNo; //订单单号
public int productCount; //商品数量
public BigDecimal priceTotal; //商品总价
public Date createTime; //下单时间
public User userInfo; //用户信息
//省略getter与setter方法...
}
1.1 association配置方式一
使用SQL关联语句,获取订单信息,同时关联获取用户信息。
(1)创建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="test">
<resultMap id="orderResultMap" type="com.pjb.mybatis.po.Order">
<id property="id" column="order_id"/>
<result property="orderNo" column="order_no"/>
<result property="productCount" column="product_count"/>
<result property="priceTotal" column="price_total"/>
<result property="createTime" column="order_create_time"/>
<association property="userInfo" javaType="com.pjb.mybatis.po.User">
<id property="id" column="user_id"/>
<result property="userName" column="user_name"/>
<result property="blogUrl" column="blog_url"/>
<result property="remark" column="remark"/>
<result property="createTime" column="user_create_time"/>
</association>
</resultMap>
<!-- 根据订单ID,获取订单信息 -->
<select id="getOrderById" parameterType="int" resultMap="orderResultMap">
SELECT o.id AS order_id
,o.order_no
,o.product_count
,o.price_total
,o.create_time AS order_create_time
,u.id AS user_id
,u.user_name
,u.blog_url
,u.remark
,u.create_time AS user_create_time
FROM tb_order o
LEFT JOIN tb_user u ON o.user_id = u.id
WHERE o.id = #{id}
</select>
</mapper>
(2)编写执行方法。
/**
* 根据订单ID,获取订单信息与关联用户信息
* @author pan_junbiao
*/
@Test
public void getOrderById()
{
DataConnection dataConnection = new DataConnection();
SqlSession sqlSession = dataConnection.getSqlSession();
Order order = sqlSession.selectOne("test.getOrderById", 1);
if (order != null)
{
System.out.println("---------------1、订单信息---------------");
System.out.println("订单编号:" + order.getId());
System.out.println("订单单号:" + order.getOrderNo());
System.out.println("商品数量:" + order.getProductCount());
System.out.println("商品总价:" + order.getPriceTotal());
System.out.println("下单时间:" + order.getCreateTime());
//获取关联的用户信息
User user = order.getUserInfo();
System.out.println("---------------2、用户信息---------------");
System.out.println("用户编号:" + order.getId());
System.out.println("用户姓名:" + user.getUserName());
System.out.println("博客地址:" + user.getBlogUrl());
System.out.println("备注信息:" + user.getRemark());
System.out.println("注册时间:" + user.getCreateTime());
}
sqlSession.close();
}
执行结果:
最终通过resultMap拿到的查询结果,是一个包含user对象信息的Order包装类。
1.2 association配置方式二
当然,如果之前已经定义好了User类对应的resultMap,那么可以在查询结果集配置中引用外部的resultMap来使用,配置方式也使用“association”标签,只不过多设置一个resultMap的属性指向外部的resultMap标签的id。
<resultMap id="orderResultMap" type="com.pjb.mybatis.po.Order">
<id property="id" column="order_id"/>
<result property="orderNo" column="order_no"/>
<result property="productCount" column="product_count"/>
<result property="priceTotal" column="price_total"/>
<result property="createTime" column="order_create_time"/>
<association property="userInfo" resultMap="userResultMap"/>
</resultMap>
<resultMap id="userResultMap" type="com.pjb.mybatis.po.User">
<id property="id" column="user_id"/>
<result property="userName" column="user_name"/>
<result property="blogUrl" column="blog_url"/>
<result property="remark" column="remark"/>
<result property="createTime" column="user_create_time"/>
</resultMap>
<!-- 根据订单ID,获取订单信息 -->
<select id="getOrderById" parameterType="int" resultMap="orderResultMap">
SELECT o.id AS order_id
,o.order_no
,o.product_count
,o.price_total
,o.create_time AS order_create_time
,u.id AS user_id
,u.user_name
,u.blog_url
,u.remark
,u.create_time AS user_create_time
FROM tb_order o
LEFT JOIN tb_user u ON o.user_id = u.id
WHERE o.id = #{id}
</select>
1.3 association配置方式三
如果不使用表的关联关系,而是通过两个SQL查询语句来分别获取订单信息、用户信息。
首先建议开启延迟加载,在MyBatis的全局配置文件SqlMapConfig.xml(mybatis-config.xml)中的<settings>标签中添加“开启延迟加载”配置信息:
<!-- 开启延迟加载开关 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 将积极加载改为消极加载(即按需加载),默认值就是false -->
<setting name="aggressiveLazyLoading" value="false"/>
修改获取订单信息的配置信息,如下:
<resultMap id="orderResultMap" type="com.pjb.mybatis.po.Order">
<id property="id" column="id"/>
<result property="orderNo" column="order_no"/>
<result property="productCount" column="product_count"/>
<result property="priceTotal" column="price_total"/>
<result property="createTime" column="create_time"/>
<association property="userInfo" select="getUserById" column="user_id"/>
</resultMap>
<resultMap id="userResultMap" type="com.pjb.mybatis.po.User">
<id property="id" column="id"/>
<result property="userName" column="user_name"/>
<result property="blogUrl" column="blog_url"/>
<result property="remark" column="remark"/>
<result property="createTime" column="create_time"/>
</resultMap>
<!-- 根据订单ID,获取订单信息 -->
<select id="getOrderById" parameterType="int" resultMap="orderResultMap">
SELECT * FROM tb_order WHERE id = #{id}
</select>
<!-- 根据用户编号,获取用户实体 -->
<select id="getUserById" parameterType="int" resultMap="userResultMap">
SELECT * FROM tb_user WHERE id = #{id}
</select>
注意:在上述配置中,association标签中不要在写resultMap属性了,否则会报错!
执行结果:
2、collection属性(集合的嵌套结果)
第二个属性是“集合的嵌套结果”,该属性的标签名称为“collection”。在一些查询结果包装类中,包含一些List集合属性,使用collection标签可以声明该List集合中属性的类型,便于MyBatis对包装类中的集合类型属性进行映射。
【示例】获取订单信息,同时获取该订单关联的商品列表。
(1)在数据库中创建订单商品信息表(tb_order_product),并添加数据。
-- 创建“订单商品信息”数据表
CREATE TABLE IF NOT EXISTS tb_order_product
(
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '订单商品编号',
order_no VARCHAR(50) NOT NULL COMMENT '订单单号',
product_id INT DEFAULT '0' COMMENT '商品ID',
product_name VARCHAR(50) NOT NULL COMMENT '商品名称',
classify_no VARCHAR(50) NOT NULL COMMENT '分类编号',
classify_name VARCHAR(50) NOT NULL COMMENT '分类名称',
product_count INT DEFAULT '0' COMMENT '商品数量',
price DECIMAL(9,2) DEFAULT '0' COMMENT '商品单价'
) COMMENT = '订单商品信息表';
-- 添加数据
INSERT INTO tb_order_product(order_no,product_id,product_name,classify_no,classify_name,product_count,price) VALUE('T20191209001',1,'华为手机','1001','手机',2,1599);
INSERT INTO tb_order_product(order_no,product_id,product_name,classify_no,classify_name,product_count,price) VALUE('T20191209001',2,'联想电脑','1002','电脑',3,3699);
(2)创建订单商品信息持久化类(OrderProduct.java)和订单信息持久化类(Order.java)。
package com.pjb.mybatis.po;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 订单商品信息持久化类
* @author pan_junbiao
**/
public class OrderProduct implements Serializable
{
public int id; //订单商品编号
public String orderNo; //订单单号
public int productId; //商品ID
public String productName; //商品名称
public String classifyNo; //分类编号
public String classifyName; //分类名称
public int productCount; //商品数量
public BigDecimal price; //商品单价
//省略getter与setter方法...
}
package com.pjb.mybatis.po;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
/**
* 订单信息持久化类
* @author pan_junbiao
**/
public class Order implements Serializable
{
public int id; //订单ID
public String orderNo; //订单单号
public int productCount; //商品数量
public BigDecimal priceTotal; //商品总价
public Date createTime; //下单时间
public List<OrderProduct> orderProductList; //订单商品列表
//省略getter与setter方法...
}
(3)编写OrderMapper.xml配置文件。
<resultMap id="orderResultMap" type="com.pjb.mybatis.po.Order">
<id property="id" column="id"/>
<result property="orderNo" column="order_no"/>
<result property="productCount" column="product_count"/>
<result property="priceTotal" column="price_total"/>
<result property="createTime" column="create_time"/>
<collection property="orderProductList" column="order_no" select="getOrderProductList" ofType="com.pjb.mybatis.po.OrderProduct"/>
</resultMap>
<!-- 根据订单ID,获取订单信息 -->
<select id="getOrderById" parameterType="int" resultMap="orderResultMap">
SELECT * FROM tb_order WHERE id = #{id}
</select>
<!-- 根据订单编号,获取订单关联商品列表 -->
<select id="getOrderProductList" parameterType="string" resultType="com.pjb.mybatis.po.OrderProduct">
SELECT * FROM tb_order_product WHERE order_no = #{orderNo}
</select>
(4)编写执行方法。
/**
* 根据订单ID,获取订单信息与关联商品信息
* @author pan_junbiao
*/
@Test
public void getOrderById()
{
DataConnection dataConnection = new DataConnection();
SqlSession sqlSession = dataConnection.getSqlSession();
Order order = sqlSession.selectOne("test.getOrderById", 1);
if (order != null)
{
System.out.println("---------------1、订单信息---------------");
System.out.println("订单编号:" + order.getId());
System.out.println("订单单号:" + order.getOrderNo());
System.out.println("商品数量:" + order.getProductCount());
System.out.println("商品总价:" + order.getPriceTotal());
System.out.println("下单时间:" + order.getCreateTime());
//获取关联的订单商品列表
List<OrderProduct> orderProductList = order.getOrderProductList();
System.out.println("---------------2、订单商品列表---------------");
for (OrderProduct product :orderProductList)
{
System.out.println("商品编号:" + product.getProductId());
System.out.println("商品名称:" + product.getProductName());
System.out.println("分类编号:" + product.getClassifyNo());
System.out.println("分类名称:" + product.getClassifyName());
System.out.println("商品数量:" + product.getProductCount());
System.out.println("商品单价:" + product.getPrice());
System.out.println("------------------------------");
}
}
sqlSession.close();
}
执行结果:
通过配置“集合的嵌套结果”,就可以将查询结果中的包装类的集合类型的属性嵌套到结果集中。当然同“association”标签一样,“collection”标签也可以引入外部的resultMap配置。如下:
<resultMap id="orderResultMap" type="com.pjb.mybatis.po.Order">
<id property="id" column="id"/>
<result property="orderNo" column="order_no"/>
<result property="productCount" column="product_count"/>
<result property="priceTotal" column="price_total"/>
<result property="createTime" column="create_time"/>
<collection property="orderProductList" column="order_no" select="getOrderProductList"/>
</resultMap>
<resultMap id="orderProductResultMap" type="com.pjb.mybatis.po.OrderProduct">
<id property="id" column="id"/>
<result property="orderNo" column="order_no"/>
<result property="productId" column="product_id"/>
<result property="productName" column="product_name"/>
<result property="classifyNo" column="classify_no"/>
<result property="classifyName" column="classify_name"/>
<result property="productCount" column="product_count"/>
<result property="price" column="price"/>
</resultMap>
<!-- 根据订单ID,获取订单信息 -->
<select id="getOrderById" parameterType="int" resultMap="orderResultMap">
SELECT * FROM tb_order WHERE id = #{id}
</select>
<!-- 根据订单编号,获取订单关联商品列表 -->
<select id="getOrderProductList" parameterType="string" resultMap="orderProductResultMap">
SELECT * FROM tb_order_product WHERE order_no = #{orderNo}
</select>
3、discriminator(鉴别器)
在MyBatis的SQL查询结果集中,有时候需要根据某个字段的值,来决定关联哪种结果集,此时就是需要使用“discriminator(鉴别器)”来实现。
【示例】获取商品信息,并根据不同的商品分类获取对应的附加信息。
(1)在数据库中创建手机信息表(tb_mobile_info)和电脑信息表(tb_computer_info),并添加数据。
-- 创建“手机信息”数据表
CREATE TABLE IF NOT EXISTS tb_mobile_info
(
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
product_id INT DEFAULT '0' COMMENT '商品ID',
camera_pixels VARCHAR(50) COMMENT '相机像素',
battery_capacity VARCHAR(50) COMMENT '电池容量'
) COMMENT = '手机信息表';
-- 添加数据
INSERT INTO tb_mobile_info(product_id,camera_pixels,battery_capacity) VALUE(1,'4800万像素','4000mAh');
-- 创建“电脑信息”数据表
CREATE TABLE IF NOT EXISTS tb_computer_info
(
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
product_id INT DEFAULT '0' COMMENT '商品ID',
processor_model VARCHAR(50) COMMENT '处理器',
graphics_card VARCHAR(50) COMMENT '显卡'
) COMMENT = '电脑信息表';
-- 添加数据
INSERT INTO tb_computer_info(product_id,processor_model,graphics_card) VALUE(2,'Intel i7','RTX2060');
(2)创建商品信息久化类(Product.java)。
package com.pjb.mybatis.po;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.HashMap;
/**
* 商品信息久化类
* @author pan_junbiao
**/
public class Product implements Serializable
{
public int productId; //商品编号
public String productName; //商品名称
public String classifyNo; //分类编号
public String classifyName; //分类名称
public BigDecimal price; //商品单价
public HashMap additiveAttributes; //商品附加属性
//省略getter与setter方法...
}
(3)编写ProductMapper.xml配置文件。
<!-- 商品信息resultMap -->
<resultMap id="productResultMap" type="com.pjb.mybatis.po.Product">
<id property="productId" column="product_id"/>
<result property="productName" column="product_name"/>
<result property="classifyNo" column="classify_no"/>
<result property="classifyName" column="classify_name"/>
<result property="price" column="price"/>
<association property="additiveAttributes" javaType="java.util.HashMap">
<discriminator javaType="string" column="classify_no">
<!-- 1001 手机 -->
<case value="1001" resultMap="mobileResultMap"/>
<!-- 1002 电脑 -->
<case value="1002" resultMap="computerResultMap"/>
</discriminator>
</association>
</resultMap>
<!-- 手机附加信息resultMap -->
<resultMap id="mobileResultMap" type="java.util.HashMap">
<!-- 相机像素 -->
<result property="cameraPixels" column="camera_pixels"/>
<!-- 电池容量 -->
<result property="batteryCapacity" column="battery_capacity"/>
</resultMap>
<!-- 电脑附加信息resultMap -->
<resultMap id="computerResultMap" type="java.util.HashMap">
<!-- 处理器 -->
<result property="processorModel" column="processor_model"/>
<!-- 显卡 -->
<result property="graphicsCard" column="graphics_card"/>
</resultMap>
<!-- 获取手机商品信息 -->
<select id="getMobileProductInfo" parameterType="int" resultMap="productResultMap">
SELECT p.product_id
,p.product_name
,p.classify_no
,p.classify_name
,p.price
,m.camera_pixels
,m.battery_capacity
FROM tb_order_product p
LEFT JOIN tb_mobile_info m ON p.product_id = m.product_id
WHERE p.product_id = #{id}
</select>
<!-- 获取电脑商品信息 -->
<select id="getComputerProductInfo" parameterType="int" resultMap="productResultMap">
SELECT p.product_id
,p.product_name
,p.classify_no
,p.classify_name
,p.price
,c.processor_model
,c.graphics_card
FROM tb_order_product p
LEFT JOIN tb_computer_info c ON p.product_id = c.product_id
WHERE p.product_id = #{id}
</select>
(4)编写执行方法,获取手机商品信息。
/**
* 获取手机商品信息
* @author pan_junbiao
*/
@Test
public void getMobileProductInfo()
{
DataConnection dataConnection = new DataConnection();
SqlSession sqlSession = dataConnection.getSqlSession();
Product product = sqlSession.selectOne("test.getMobileProductInfo", 1);
if (product != null)
{
System.out.println("---------------1、商品信息---------------");
System.out.println("商品编号:" + product.getProductId());
System.out.println("商品名称:" + product.getProductName());
System.out.println("分类编号:" + product.getClassifyNo());
System.out.println("分类名称:" + product.getClassifyName());
System.out.println("商品单价:" + product.getPrice());
//获取商品附加信息
System.out.println("---------------2、商品附加信息---------------");
HashMap additiveAttributes = product.getAdditiveAttributes();
if(additiveAttributes!=null)
{
System.out.println("相机像素:" + additiveAttributes.get("cameraPixels"));
System.out.println("电池容量:" + additiveAttributes.get("batteryCapacity"));
}
}
sqlSession.close();
}
执行结果:
(5)编写执行方法,获取电脑商品信息。
/**
* 获取电脑商品信息
* @author pan_junbiao
*/
@Test
public void getComputerProductInfo()
{
DataConnection dataConnection = new DataConnection();
SqlSession sqlSession = dataConnection.getSqlSession();
Product product = sqlSession.selectOne("test.getComputerProductInfo", 2);
if (product != null)
{
System.out.println("---------------1、商品信息---------------");
System.out.println("商品编号:" + product.getProductId());
System.out.println("商品名称:" + product.getProductName());
System.out.println("分类编号:" + product.getClassifyNo());
System.out.println("分类名称:" + product.getClassifyName());
System.out.println("商品单价:" + product.getPrice());
//获取商品附加信息
System.out.println("---------------2、商品附加信息---------------");
HashMap additiveAttributes = product.getAdditiveAttributes();
if(additiveAttributes!=null)
{
System.out.println("处理器:" + additiveAttributes.get("processorModel"));
System.out.println("显卡:" + additiveAttributes.get("graphicsCard"));
}
}
sqlSession.close();
}
执行结果:
从执行结果中可以看到,当 classify_no(分类编号)字段的结果为1001时,加载的是手机附加信息resultMap,而当字段的结果为1002时,加载的是电脑附加信息resultMap。这样可以根据不同的情况,映射不同的结果集。