我都知道Java对象存在三种关联关系:一对一、一对多、多对多。本文将详细讲解MyBatis的关联查询方式。
关联元素
- 一对一关系:association
- 一对多关系:collection
- 鉴别器映射:discriminator
查询方式
无论是一对一还是一对多都存在两种查询方式
嵌套结果:通过查询结果进行映射至相应的JavaBean(例如连接查询)
嵌套查询:先查一张表,然后通过这张表的结果去查其他表
数据库表关系
现在有三张表,表字段如下
- company:
| id | code | name | source | create_time |
|---|
- company_ext:
| id | remark | create_time |
|---|
- company_price:
| id | company_id | date | price | create_time | update_time |
|---|
其中company.id与company_ext.id对应,且为一对一的关系。
company.id与company_price.company_id对应,即为一对多的关系。
JavaBean
这里只列出属性名,set、get等方法,省略
public class Company {
private String id;
private String code;
private String name;
private String source;
private Date createTime;
......
}
public class CompanyExt {
private String id;
private String remark;
private Date createTime;
......
}
public class CompanyAndExt {
private String id;
private String code;
private String name;
private String source;
private Date createTime;
private CompanyExt companyExt;
......
}
public class StockPrice {
private String id;
private String companyId;
private String date;
private String price;
private String createTime;
......
}
public class CompanyPrice {
private String id;
private String code;
private String name;
private String source;
private Date createTime;
private List<StockPrice> stockPriceList;
......
}
接口类:
package com.test.mapper;
import java.util.List;
import com.test.dto.*;
import org.apache.ibatis.annotations.Param;
public interface CompanyMapper {
// 一对一
// 嵌套结果、嵌套查询
CompanyAndExt getCompanyAndExtById(String id);
// 嵌套查询
CompanyExt getCompanyExtById(String id);
// 一对多
// 嵌套结果、嵌套查询
CompanyPrice getCompanyPriceById(String id);
// 嵌套查询
List<StockPrice> listStockPriceByCompanyId(String companyId);
}
一对一(association)
- 嵌套结果
a. association常用元素如下
property:所映射的属性名
javaType:所映射的属性对应的Java属性类型
resultMap:使用已有的resultMap
columnPrefix:子标签result(id)的column的前缀
b. 示例源码
<!--一对一的嵌套结果-->
<resultMap id="companyResultMap" type="com.test.dto.Company">
<id property="id" column="id"/>
<result property="code" column="code"/>
<result property="name" column="name"/>
<result property="source" column="source"/>
<result property="createTime" column="create_time"/>
</resultMap>
<resultMap id="companyAndExtResultMap" extends="companyResultMap" type="com.test.dto.CompanyAndExt">
<association property="companyExt" javaType="com.test.dto.CompanyExt" columnPrefix="ext_">
<id column="id" property="id" />
<result column="remark" property="remark" />
<result column="create_time" property="createTime" />
</association>
</resultMap>
<select id="getCompanyAndExtById" resultMap="companyAndExtResultMap">
SELECT c.`id`,c.`code`,c.`name`,c.`source`,c.`create_time`, ce.`id` ext_id, ce.`remark` ext_remark, ce.`create_time` ext_create_time FROM company c, company_ext ce WHERE c.`id` = ce.`id` AND c.`id` = #{id,jdbcType=VARCHAR};
</select>
补充:
resultMap 下的extends元素,顾名思义,可以继承另外一个resultMap的映射配置
关联表查询时,使用前缀对后期的维护提供很大的便利
- 嵌套查询
a. association常用元素如下
select:使用嵌套查询方法
column:主查询的结果作为嵌套查询的参数
fetchType:是否使用懒加载,'lazy'为懒加载,'eager'为积极加载
b.示例代码
<!--一对一的嵌套查询-->
<resultMap id="companyExt" type="com.test.dto.CompanyExt">
<id column="id" property="id"/>
<result column="remark" property="remark"/>
<result column="create_time" property="createTime"/>
</resultMap>
<sql id="companyExtSelectColumns">
`id`,`remark`,`create_time`
</sql>
<select id="getCompanyExtById" resultMap="companyExt">
SELECT
<include refid="companyExtSelectColumns"/>
FROM company_ext WHERE id = #{id,jdbcType=VARCHAR};
</select>
<resultMap id="companyAndExtResultMap" extends="companyResultMap" type="com.test.dto.CompanyAndExt">
<association property="companyExt" fetchType="lazy" column="id" javaType="com.test.dto.CompanyExt"
select="com.test.mapper.CompanyMapper.getCompanyExtById"/>
</resultMap>
<select id="getCompanyAndExtById" resultMap="companyAndExtResultMap">
SELECT
<include refid="selectColumns"/>
FROM company c WHERE c.id = #{id,jdbcType=INTEGER}
</select>
补充说明:
column属性:column="id",表示查询结果与嵌套方法入参名称相同,且入参只有一个。多个参数使用column="{resultId=id,resultCode=code}",resultId、resultCode都为主查询的结果名
fetchType属性:fetchType="lazy" 表示使用懒加载,即程序中实际使用了CompanyAndExt.CompanyExt里的属性或方法,才进行查询,这样会大大增加查询效率。
一对多(collection)
collection支持的属性及其属性的作用于association完全相同(详情可见上文)
- 嵌套结果
a. myBatis中的javaType与ofType的区别
都是用于指定对象类型,javaType是用来指定映射pojo中的属性的类型,而ofType是用来指定映射至集合中的pojo类型。
b. 示例源码
<!--一对多的嵌套结果-->
<resultMap id="companyResultMap" type="com.test.dto.Company">
<id property="id" column="id"/>
<result property="code" column="code"/>
<result property="name" column="name"/>
<result property="source" column="source"/>
<result property="createTime" column="create_time"/>
</resultMap>
<resultMap id="companyPriceResultMap" extends="companyResultMap" type="com.test.dto.CompanyPrice">
<collection property="stockPriceList" ofType="com.test.dto.StockPrice" columnPrefix="cp_">
<id column="id" property="id"/>
<result column="company_id" property="companyId"/>
<result column="date" property="date"/>
<result column="price" property="price"/>
<result column="create_time" property="createTime"/>
</collection>
</resultMap>
<select id="getCompanyPriceById" resultMap="companyPriceResultMap">
SELECT
c.`id`,
c.`name`,
c.`code`,
c.`source`,
c.`create_time`,
cp.`id` AS cp_id,
cp.`company_id` AS cp_company_id,
cp.`date` AS cp_date,
cp.`price` AS cp_price,
cp.`create_time` AS cp_create_time
FROM
company AS c ,
company_price AS cp
WHERE
c.`id` = cp.`company_id` AND
c.`id` = #{id,jdbcType=VARCHAR};
</select>
- 嵌套查询
a. 示例源码
<!--一对多的嵌套查询-->
<resultMap id="stockPriceResultMap" type="com.test.dto.StockPrice">
<id column="id" property="id"/>
<result column="company_id" property="companyId"/>
<result column="date" property="date"/>
<result column="price" property="price"/>
<result column="create_time" property="createTime"/>
</resultMap>
<select id="listStockPriceByCompanyId" resultMap="stockPriceResultMap">
SELECT
cp.`id`,
cp.`company_id`,
cp.`date`,
cp.`price`,
cp.`create_time`
FROM
company_price AS cp
WHERE
cp.company_id = #{companyId,jdbcType=VARCHAR}
</select>
<resultMap id="companyPriceResultMap" extends="companyResultMap" type="com.test.dto.CompanyPrice">
<collection property="stockPriceList" column="id" fetchType="lazy"
select="com.test.mapper.CompanyMapper.listStockPriceByCompanyId"/>
</resultMap>
<select id="getCompanyPriceById" resultMap="companyPriceResultMap">
SELECT
<include refid="selectColumns"/>
FROM company c WHERE c.id = #{id,jdbcType=VARCHAR}
</select>
鉴别器映射(discriminator)
鉴别器非常的容易理解,因为,它非常像java中的switch语句
我们直接来看示例代码,然后根据示例代码进行讲解
<resultMap id="companyPriceResultMap1" type="com.test.dto.CompanyPrice">
......
</resultMap>
<resultMap id="companyPriceResultMap2" type="com.test.dto.CompanyPrice">
......
</resultMap>
<resultMap id="companyPriceResultMap" type="com.test.dto.CompanyPrice">
<discriminator javaType="int" column="code">
<case value="1" resultMap="companyPriceResultMap1" />
<case value="2" resultMap="companyPriceResultMap2" />
</discriminator>
</resultMap>
说明:
- discriminator标签有两个属性:
a. column:需要鉴别的列
b. javaType:指定列的数据类型,用于比较 - discriminator的子标签case有三个属性
a. value:鉴别的值
b. resultMap:当鉴别值匹配时,使用该resultMap指定的映射,resultMap优先级高于resultType
c. resultType:当鉴别值匹配时,使用该resultType指定的映射
特别说明:
- 本博客为自己的学习笔记,因此存在知识点覆盖不全,部分功能遗漏,后续逐步补充;
- 文中如果存在错误的地方,还请留言指出,我会第一时间纠正;
- 如果有什么不满(需要补充的知识点、优化点等),还请不吝指教,我会尽快响应处理,谢谢!