Mybatis复杂类型的结果映射

专栏精选

Mybatis的快速入门

引入Mybatis

Mybatis的增删改查扩展功能说明

mapper映射的参数和结果

文章目录

  • 专栏精选
  • 摘要
  • 引言
  • 正文
    • association标签的简单查询
    • association标签的嵌套查询
    • collection集合类型的嵌套查询
    • collection集合类型的简单查询
    • collection集合类型的多结果集查询
  • 总结

摘要

在MyBatis框架中,Association标签和Collection标签是处理对象关系映射的关键组件。它们能够以简洁明了的方式映射实体类与数据库表之间的关系。通过这两个标签,我们可以实现一对一(One-to-One)、一对多(One-to-Many)和多对多(Many-to-Many)的关系映射,从而简化了数据库操作和对象关系之间的复杂转换。

引言

你是否曾经遇到过在Mybatis中处理复杂的对象关系映射时感到困扰?是否曾经遇到过一个查询功能的SQL过长而感到无从下手?是否希望找到一种简单而高效的方式来映射实体类与数据库表之间的关系?如果你对这些话题感兴趣,那么本文将为你提供一种简单有效的解决方案。

在阅读本文之前,我们希望你能积极参与并与我们一起探讨。请随时在评论区分享你的见解、疑问或经验。你的参与不仅能帮助我们更好地理解你的需求,还能为其他读者提供宝贵的参考意见。做好准备,Let’s go

正文

首图

针对以下业务逻辑, 编码/状态/类型 属性一般通过字典值保存, 业务表中存储code, 字典表中存储对应的名称, 如何在一次查询中直接获取到对应的字典编码和名称呢

Mybatis复杂类型的结果映射_第1张图片

association标签的简单查询

复杂类型映射需要通过 标签实现,该标签支持通过数据关系构建复杂对象。

为了方便测试,在数据库中新增字典表

create table dict_test  
(  
    dict_name varchar(20)   not null comment '名称',  
    dict_code char          not null comment '编码',  
    dict_type varchar(50)   not null comment '类型',  
    dict_sort int default 0 not null comment '排序'  
)  
    comment '字典表-测试用';

新增对应的实体类

public class DictTest {  
      
    private String dictName;  
  
    private String dictCode;  
  
    private String dictType;  
  
    private Integer dictSort;
    //getter、setter、toString方法省略
}

修改AppTestEntity 类,构建为复杂对象

public class AppTestEntity {  
    private Long id;  
    private String appName;  
    private String appCode;  
    private String authType;  
    private LocalDate createDate;  
    private String creator;  
    private String appStatus;
  
    private DictTest authTypeDict;
	//省略getter、setter、toString方法
}

新增mapper接口方法

public interface SimpleQueryMapper {
	AppTestEntity queryAppDetail(@Param("id") Integer id);
}

映射文件

<resultMap id="appDetail" type="appTestEntity" autoMapping="true">  
	
    <association property="authTypeDict" javaType="dictTest">  
        <id column="auth_type_dc" property="dictCode"/>  
        <id column="auth_type_dt" property="dictType"/>  
        <result column="auth_type_dn" property="dictName"/>  
        <result column="auth_type_ds" property="dictSort"/>  
    association>
resultMap>  
  
<select id="queryAppDetail" resultMap="appDetail">  
    select t1.*  
        ,t2.dict_code as auth_type_dc,t2.dict_name as auth_type_dn,t2.dict_type as auth_type_dt,t2.dict_sort as auth_type_ds  
    from (  
        select id,app_name,app_code,auth_type,create_date,creator,app_status from app_test where id=#{id}  
    ) t1 left join (  
        select dict_code,dict_name,dict_type,dict_sort from dict_test where dict_type='app_auth_type'  
    ) t2 on t1.auth_type=t2.dict_code  
select>

测试代码

public class SimpleQueryService extends MybatisService<SimpleQueryMapper>{  
    @Override  
    public void doService() {  
        SimpleQueryMapper mapper = super.getMapper(SimpleQueryMapper.class);  
        this.testAppAssociation(mapper);  
    }
    
    private void testAppAssociation(SimpleQueryMapper mapper){  
        AppTestEntity detail = mapper.queryAppDetail(2);  
        System.out.println(detail);  
    }  
}

打印结果

AppTestEntity{id=2, appName='公共应用', appCode='common', authType='2', createDate=2023-10-31, creator='admin2', authTypeDict=DictTest{dictName='手机号', dictCode='2', dictType='app_auth_type', dictSort=2}}

association标签的嵌套查询

以上就是 标签的简单用法,该标签还支持在 标签

<resultMap id="appDetail" type="appTestEntity" autoMapping="true">  
	
	<result column="app_status" property="appStatus"/>
    <association property="authTypeDict" javaType="dictTest">  
        <id column="auth_type_dc" property="dictCode"/>  
        <id column="auth_type_dt" property="dictType"/>  
        <result column="auth_type_dn" property="dictName"/>  
        <result column="auth_type_ds" property="dictSort"/>  
    association>    
    
    <association property="appStatusDict" javaType="dictTest" select="queryAppStatus" column="app_status">association>  
resultMap>

<select id="queryAppStatus" resultType="dictTest">  
    select dict_code,dict_name,dict_type,dict_sort from dict_test  
    where dict_type='app_status' and dict_code=#{code}  
select>

打印结果如下:

AppTestEntity{id=2, appName='公共应用', appCode='common', authType='2', createDate=2023-10-31, creator='admin2', appStatus='0', authTypeDict=DictTest{dictName='手机号', dictCode='2', dictType='app_auth_type', dictSort=2}, appStatusDict=DictTest{dictName='临时应用', dictCode='0', dictType='app_status', dictSort=0}}

针对以下一对多关系(应用明细 和 服务列表)的关联查询有需要如何实现呢?

Mybatis复杂类型的结果映射_第2张图片

collection集合类型的嵌套查询

以上这个业务是一个典型的详情展示页面,其中的“应用”和“服务”存在一对多关联,需要在AppTestEntity类中添加List类型的属性,那么如何通过标签表示这种对应关系呢?

添加数据库表

create table service_test  
(  
    id           int auto_increment comment '主键ID'  
        primary key,  
    service_name varchar(50)  not null comment '服务名称',  
    service_code varchar(50)  not null comment '服务编码',  
    service_path varchar(100) not null comment '资源地址',  
    app_id       int          null comment '应用ID'  
)  
    comment '微服务表';

生成实体类

public class ServiceTestEntity {  
    private Long id;  
    private String serviceName;  
    private String serviceCode;  
    private String servicePath;  
    private Long appId;
	//getter, setter, toString方法省略
}

public class AppTestEntity {  
    private Long id;  
    private String appName;  
    private String appCode;  
    private String authType;  
    private LocalDate createDate;  
    private String creator;  
    private String appStatus;  
  
    private DictTest authTypeDict;  
    private DictTest appStatusDict;  
  
    private List<ServiceTestEntity> services;
    //getter, setter, toString
}

映射文件的写法

<resultMap id="appDetail" type="appTestEntity" autoMapping="true">  
    <result column="app_status" property="appStatus"/>  
    <association property="authTypeDict" javaType="dictTest">  
        <id column="auth_type_dc" property="dictCode"/>  
        <id column="auth_type_dt" property="dictType"/>  
        <result column="auth_type_dn" property="dictName"/>  
        <result column="auth_type_ds" property="dictSort"/>  
    association>    
    <association property="appStatusDict" javaType="dictTest" select="queryAppStatus" column="app_status"/>
    <collection property="services" column="id" select="queryServices" ofType="serviceTestEntity"/>  
resultMap>

<select id="queryServices" resultType="serviceTestEntity">  
    select * from service_test where app_id=#{appId}  
select>

其他内容不变, 打印结果如下:

AppTestEntity{id=null, appName='公共应用', appCode='common', authType='2', createDate=2023-10-31, creator='admin2', appStatus='0', authTypeDict=DictTest{dictName='手机号', dictCode='2', dictType='app_auth_type', dictSort=2}, appStatusDict=null, services=[ServiceTestEntity{id=1, serviceName='配置中心', serviceCode='config-center-service', servicePath='/config', appId=2}, ServiceTestEntity{id=2, serviceName='注册中心', serviceCode='eureka-service', servicePath='/eureka', appId=2}]}

collection集合类型的简单查询

标签一样, 标签也支持嵌套select和单个sql映射, 以上为嵌套select映射, 下面介绍单个sql映射
collections标签的普通查询

  1. mapper接口添加新方法
  2. 映射文件添加sql
  3. 测试类
AppTestEntity queryAppServices(Integer id);
<resultMap id="appServices" type="appTestEntity" autoMapping="true">  
	
    <id column="id" property="id"/>  
    <collection property="services" ofType="serviceTestEntity" autoMapping="true">  
        <id column="service_id" property="id"/>  
    collection>
resultMap>  
<select id="queryAppServices" resultMap="appServices">  
    select t1.*,t2.id as service_id,t2.service_name,t2.service_code,t2.service_path,t2.app_id  
    from app_test t1 left join service_test t2 on t1.id=t2.app_id where t1.id=#{id}  
select>
public class SimpleQueryService extends MybatisService<SimpleQueryMapper>{  
    @Override  
    public void doService() {  
        SimpleQueryMapper mapper = super.getMapper(SimpleQueryMapper.class);  
        this.testAppServices(mapper);  
    }
    
	private void testAppServices(SimpleQueryMapper mapper){  
	    AppTestEntity detail=mapper.queryAppServices(2);  
	    System.out.println(detail);  
	}
}

打印结果如下:

AppTestEntity{id=2, appName='公共应用', appCode='common', authType='2', createDate=2023-10-31, creator='admin2', appStatus='0', authTypeDict=null, appStatusDict=null, services=[ServiceTestEntity{id=1, serviceName='配置中心', serviceCode='config-center-service', servicePath='/config', appId=2}, ServiceTestEntity{id=2, serviceName='注册中心', serviceCode='eureka-service', servicePath='/eureka', appId=2}]}

collection集合类型的多结果集查询

除了以上方式之外, mybatis还提供基于存储过程的多结果集查询, 即在存储过程中通过执行两个sql获取到对应的查询结果。需要注意的是,这种将业务逻辑代码存储到数据库中的方式在开发过程中应严格控制使用

样例代码如下:

数据库新增存储过程

drop procedure if exists queryAppServices;  
create procedure queryAppServices(appId int) begin  
    select * from app_test where id=appId;  
    select id as service_id,service_code,service_name,service_path,app_id from service_test where app_id=appId;  
end;

mapper层添加新方法

//注意:只存在一个基本类型包装类的参数时,可以不用@Param注解说明参数名称,可直接使用形参的名称
AppTestEntity queryAppServicesProcedure(Integer id);
<resultMap id="appServices" type="appTestEntity" autoMapping="true">  
    <id column="id" property="id"/>  
    
    <collection property="services" ofType="serviceTestEntity" resultSet="services" autoMapping="true">  
        <id column="service_id" property="id"/>  
    collection>
resultMap>


<select id="queryAppServicesProcedure" resultSets="app,services" resultMap="appServices" >  
    {call queryAppServices(#{id, jdbcType=INTEGER, mode=IN})}  
select>

测试类代码

private void testAppServicesProcedure(SimpleQueryMapper mapper){  
    AppTestEntity detail=mapper.queryAppServicesProcedure(2);  
    System.out.println(detail);  
}

打印结果

AppTestEntity{id=2, appName='公共应用', appCode='common', authType='2', createDate=2023-10-31, creator='admin2', appStatus='0', authTypeDict=null, appStatusDict=null, services=[ServiceTestEntity{id=1, serviceName='配置中心', serviceCode='config-center-service', servicePath='/config', appId=2}, ServiceTestEntity{id=2, serviceName='注册中心', serviceCode='eureka-service', servicePath='/eureka', appId=2}]}

以上的resultMap标签可以等价写成如下两个,效果相同, 注意select标签的resultMap属性需要同步修改

<resultMap id="appServiceCall" type="appTestEntity" autoMapping="true">  
    <id column="id" property="id"/>  
    <collection property="services" resultSet="services" resultMap="serviceCall"/>  
resultMap>  
<resultMap id="serviceCall" type="serviceTestEntity" autoMapping="true">  
    <id column="service_id" property="id"/>  
resultMap>

总结

在这篇文章中,我们认识了Mybatis中 标签和 标签的基本用法,这两个标签是mybatis复杂类型结果映射的基础。灵活使用它们可以使我们开发的代码更加清晰易懂,能够在一定程度上减少业务SQL过长的现象。

经过今天的学习,在这篇文章中提出的疑问3也得到了解决


联系方式
邮箱:[email protected]

❗版权声明
本文为原创文章,版权归作者所有。未经许可,禁止转载。更多内容请访问奇迹老李的博客首页

你可能感兴趣的:(Mybatis,mybatis,数据库)