MyBatis-Plus 不支持联表?一个依赖轻松搞定

「 关注“石杉的架构笔记”,大厂架构经验倾囊相授 

MyBatis-Plus 不支持联表?一个依赖轻松搞定_第1张图片

文章来源:【公众号:一行Java】

2a88888900a5adf0b7f1c702fc5e24c5.gif

前言

 MyBatis-Plus 不支持联表,遇到复杂查询依然还是麻烦,怎么办?

MyBatis-Plus 要想实现联表查询,只需要引入一个依赖mybatis-plus-join,就能完美解决。


准备

本文,需要你对 MyBatis-Plus 有一定的了解~

示例源码地址:https://github.com/vehang/ehang-spring-boot/tree/main/spring-boot-010-mysql-mybatis-plus  (

联表查询所有测试用例全部在Test目录下)

MyBatis Plus Join

MyBatis Plus Join一款专门解决MyBatis Plus 关联查询问题的扩展框架,他并不一款全新的框架,而是基于MyBatis Plus功能的增强,所以MyBatis Plus的所有功能MyBatis Plus Join同样拥有;框架的使用方式和MyBatis Plus一样简单,几行代码就能实现联表查询的功能

官方仓库:https://gitee.com/best_handsome/mybatis-plus-join


依赖

  • mybatis

    
        com.baomidou
        mybatis-plus-boot-starter
        3.4.3.4
    
  • 数据库连接依赖;

    大版本务必和自己的数据库版本一致

    
        mysql
        mysql-connector-java
        5.1.46
    
  • 分页

    
        com.baomidou
        mybatis-plus-extension
        3.4.1
    
  • 联表查询

    
    
        com.github.yulichang
        mybatis-plus-join
        1.3.11
    


数据库表

为了方便做联表测试,这里预先准备三张表(学校表、班级表、学生表),用来做关联查询测试,sql如下:

DROP TABLE IF EXISTS `school_info`;
CREATE TABLE `school_info`  (
  `id` int(11) NOT NULL,
  `school_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '学校名称',
  `school_addr` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '学校地址',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

INSERT INTO `school_info` VALUES (1, 'XXX小学', 'xx区xx街道80号');

-  ----------------------------------------------------------------

CREATE TABLE `class_info`  (
  `id` int(11) NOT NULL,
  `class_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '名称',
  `class_desc` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '描述',
  `school_id` int(11) NOT NULL COMMENT '隶属的学校',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

INSERT INTO `class_info` VALUES (1, '一年级1班', NULL, 1);
INSERT INTO `class_info` VALUES (2, '一年级2班', NULL, 1);

-  ----------------------------------------------------------------

CREATE TABLE `student_info`  (
  `id` int(11) NOT NULL,
  `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `age` int(11) NULL DEFAULT NULL,
  `class_id` int(11) NULL DEFAULT NULL,
  `school_id` int(11) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

INSERT INTO `student_info` VALUES (1, '张三', 7, 1, 1);
INSERT INTO `student_info` VALUES (2, '李四', 7, 2, 1);
INSERT INTO `student_info` VALUES (3, '王五', 8, 1, 1);
INSERT INTO `student_info` VALUES (4, '赵六', 8, 1, 1);


基础 MyBatis Plus 代码生成

以下的代码通过 MyBatisX 工具自动生成;

MyBatis-Plus 不支持联表?一个依赖轻松搞定_第2张图片


MyBatis Plus Join 核心类说明

  • MPJBaseMapper

    扩展了MyBatis Plus的 BaseMapper 接口

    public interface MPJBaseMapper extends BaseMapper {
        Integer selectJoinCount(@Param("ew") MPJBaseJoin var1);
    
         DTO selectJoinOne(@Param("resultTypeClass_Eg1sG") Class var1, @Param("ew") MPJBaseJoin var2);
    
        Map selectJoinMap(@Param("ew") MPJBaseJoin var1);
    
         List selectJoinList(@Param("resultTypeClass_Eg1sG") Class var1, @Param("ew") MPJBaseJoin var2);
    
        List> selectJoinMaps(@Param("ew") MPJBaseJoin var1);
    
        > IPage selectJoinPage(P var1, @Param("resultTypeClass_Eg1sG") Class var2, @Param("ew") MPJBaseJoin var3);
    
        > IPage> selectJoinMapsPage(P var1, @Param("ew") MPJBaseJoin var2);
    }
  • MPJBaseService

    扩展了MyBatis Plus的 IService 接口

    public interface MPJBaseService extends IService {
        Integer selectJoinCount(MPJBaseJoin var1);
    
         DTO selectJoinOne(Class var1, MPJBaseJoin var2);
    
         List selectJoinList(Class var1, MPJBaseJoin var2);
    
        > IPage selectJoinListPage(P var1, Class var2, MPJBaseJoin var3);
    
        Map selectJoinMap(MPJBaseJoin var1);
    
        List> selectJoinMaps(MPJBaseJoin var1);
    
        >> IPage> selectJoinMapsPage(P var1, MPJBaseJoin var2);
    }
  • MPJBaseServiceImpl

    扩展了MyBatis Plus的 ServiceImpl 接口实现

    public class MPJBaseServiceImpl, T> extends ServiceImpl implements MPJBaseService {
    ...
    }


整合 MyBatis Plus Join

简单的三处调整,就能完成整合工作

  • 将mapper改为继承MPJBaseMapper (必选)

    修改前

    public interface StudentInfoMapper extends BaseMapper {
    }

    修改后

    public interface StudentInfoMapper extends MPJBaseMapper {
    }
  • 将service改为继承MPJBaseService (可选)

    修改前

    public interface StudentInfoService extends BaseService {
    }

    修改后

    public interface StudentInfoService extends MPJBaseService {
    }
  • 将serviceImpl改为继承MPJBaseServiceImpl (可选)

    修改前

    @Service
    public class StudentInfoServiceImpl extends BaseServiceImpl
        implements StudentInfoService{
    }

    修改后

    @Service
    public class StudentInfoServiceImpl extends MPJBaseServiceImpl
        implements StudentInfoService{
    }


联表测试

测试需求:查询学生所处的班级及学校

DTO定义

用于联表查询后接收数据的实体类

@Data
public class StudentInfoDTO {
 // 学生id
    private Integer id;

    // 性名
    private String name;

    // 年龄
    private Integer age;

    // 班级名称
    private String className;

    // 学校名称
    private String schoolName;

    // 学校地址 用于测试别名
    private String scAddr;
}

单记录联表查询

@Autowired
StudentInfoService sutdentInfoService;

/**
 * 联表查询单个
 */
@Test
public void selectJoinOne() {
    StudentInfoDTO studentInfoDTO = sutdentInfoService.selectJoinOne(StudentInfoDTO.class,
            new MPJLambdaWrapper()
                    .selectAll(StudentInfo.class)
                    .select(SchoolInfo::getSchoolName)
                    .selectAs(SchoolInfo::getSchoolAddr, StudentInfoDTO::getScAddr)
                    .select(ClassInfo::getClassName)
                    .leftJoin(SchoolInfo.class, SchoolInfo::getId, StudentInfo::getSchoolId)
                    .leftJoin(ClassInfo.class, ClassInfo::getId, StudentInfo::getClassId)
                    .eq(StudentInfo::getId, 1));
    log.info("selectJoinOne:{}", JSON.toJSONString(studentInfoDTO));
}

简单说明

  • StudentInfoDTO.class

    表示resultType,用于接收联表查询之后的数据库返回;

    当返回是多个表数据的整合,需要新定义一个对象用于接收;

    当返回只需要用到一个表的数据,直接用数据库映射对象接收即可;

  • selectAll

    指明查询实体对应的所有字段

  • select

    指定查询列,同一个select只能指明单个表的列,所以多表关联时需要使用多个select去指明不同表的列

  • selectAs

    重命名,表现在sql层面是会给字段加上as(别名);主要用在数据库字段名也实体对象的名称不一致的情况;

  • leftJoin、rightJoin、innerJoin

    左链接、右连接、等值连接;

    • 参数一:参与联表的对象

    • 参数二:on关联的指定,此属性必须是第一个对象中的值

    • 参数三:参与联表on的另一个实体类属性

  • 条件构造器

    联表后可能会存在各种筛选条件,可以根据上面对条件构造器的介绍,指明所需要的筛选条件,比如上面.eq(StudentInfo::getId, 1)),就是用来指明ID为1的学生信息。

  • 表名

    默认主表别名是t,其他的表别名以先后调用的顺序使用*t1,t2,t3....*;

    需要直接apply语句的时候,就得知道对应的表名是什么,再进行添加,所以不到万不得已的时候,不建议直接追加语句。

等价SQL

SELECT 
 t.id,
 t.name,
 t.age,
 t.class_id,
 t.school_id,
 t1.school_name,
 t1.school_addr AS scAddr,
 t2.class_name
FROM 
 student_info t
 LEFT JOIN school_info t1 ON (t1.id = t.school_id)
 LEFT JOIN class_info t2 ON (t2.id = t.class_id)
WHERE (t.id = ?)

执行结果

MyBatis-Plus 不支持联表?一个依赖轻松搞定_第3张图片

多记录联表查询

@Autowired
StudentInfoService sutdentInfoService;

/**
 * 联表查询批量
 */
@Test
public void selectJoinList() {
    List studentInfoDTOS = sutdentInfoService.selectJoinList(StudentInfoDTO.class,
            new MPJLambdaWrapper()
                    .selectAll(StudentInfo.class)
                    .select(SchoolInfo::getSchoolName)
                    .selectAs(SchoolInfo::getSchoolAddr, StudentInfoDTO::getScAddr)
                    .select(ClassInfo::getClassName)
                    .leftJoin(SchoolInfo.class, SchoolInfo::getId, StudentInfo::getSchoolId)
                    .leftJoin(ClassInfo.class, ClassInfo::getId, StudentInfo::getClassId)
            //.eq(StudentInfo::getId, 1)
    );
    log.info("selectJoinList:{}", JSON.toJSONString(studentInfoDTOS));
}

等价SQL

SELECT 
 t.id,
 t.name,
 t.age,
 t.class_id,
 t.school_id,
 t1.school_name,
 t1.school_addr AS scAddr,
 t2.class_name
FROM 
 student_info t
 LEFT JOIN school_info t1 ON (t1.id = t.school_id)
 LEFT JOIN class_info t2 ON (t2.id = t.class_id)

执行结果

MyBatis-Plus 不支持联表?一个依赖轻松搞定_第4张图片

联表分页查询

@Autowired
StudentInfoService sutdentInfoService;

/**
 * 分页查询
 */
@Test
public void selectJoinPage() {
    IPage studentInfoDTOIPage = sutdentInfoService.selectJoinListPage(new Page<>(1, 2), StudentInfoDTO.class,
            new MPJLambdaWrapper()
                    .selectAll(StudentInfo.class)
                    .select(SchoolInfo::getSchoolName)
                    .selectAs(SchoolInfo::getSchoolAddr, StudentInfoDTO::getScAddr)
                    .select(ClassInfo::getClassName)
                    .leftJoin(SchoolInfo.class, SchoolInfo::getId, StudentInfo::getSchoolId)
                    .leftJoin(ClassInfo.class, ClassInfo::getId, StudentInfo::getClassId)
                    .orderByAsc(StudentInfo::getId)
    );
    log.info("selectJoinPage:{}", JSON.toJSONString(studentInfoDTOIPage));
}

等价SQL

SELECT 
 t.id,
 t.name,
 t.age,
 t.class_id,
 t.school_id,
 t1.school_name,
 t1.school_addr AS scAddr,
 t2.class_name
FROM 
 student_info t
 LEFT JOIN school_info t1 ON (t1.id = t.school_id)
 LEFT JOIN class_info t2 ON (t2.id = t.class_id)
ORDER BY 
 t.id ASC 
LIMIT 2

执行结果

MyBatis-Plus 不支持联表?一个依赖轻松搞定_第5张图片


总结

是不是简单、方便、好用!

一个依赖,就让 MyBatis-Plus 拥有联表操作,再也不用写那些繁琐的xml;不过 MyBatis Plus Join 要求 MyBatis Plus 的版本必须大于等于3.4.0,我在初次整合的时候,确实就遇到了各种兼容问题,处理了好久,但解决之后,使用起来就变的非常舒服;所以当你在决定使用他之前,可以先找一个不太重要的项目,将一些兼容问题先摸索一遍,验证一下方案可行性之后,再进一步整合。作者的技术交流群也比较的活跃,只要有问题,都会给予解答,非常值得一试!

d1b07473f6097009c1bd382891f6d26b.png

3d4dd3886d7dfd93edf355a0d057f045.png

欢迎扫码加入儒猿技术交流群,每天晚上20:00都有Java面试、Redis、MySQL、RocketMQ、SpringCloudAlibaba、Java架构等技术答疑分享,更能跟小伙伴们一起交流技术

MyBatis-Plus 不支持联表?一个依赖轻松搞定_第6张图片

另外推荐儒猿课堂的1元系列课程给您,欢迎加入一起学习~

互联网Java工程师面试突击课

(1元专享)

MyBatis-Plus 不支持联表?一个依赖轻松搞定_第7张图片

SpringCloudAlibaba零基础入门到项目实战

(1元专享)

MyBatis-Plus 不支持联表?一个依赖轻松搞定_第8张图片

亿级流量下的电商详情页系统实战项目

(1元专享)

MyBatis-Plus 不支持联表?一个依赖轻松搞定_第9张图片

Kafka消息中间件内核源码精讲

(1元专享)

MyBatis-Plus 不支持联表?一个依赖轻松搞定_第10张图片

12个实战案例带你玩转Java并发编程

(1元专享)

MyBatis-Plus 不支持联表?一个依赖轻松搞定_第11张图片

Elasticsearch零基础入门到精通

(1元专享)

MyBatis-Plus 不支持联表?一个依赖轻松搞定_第12张图片

基于Java手写分布式中间件系统实战

(1元专享)

MyBatis-Plus 不支持联表?一个依赖轻松搞定_第13张图片

基于ShardingSphere的分库分表实战课

(1元专享)

MyBatis-Plus 不支持联表?一个依赖轻松搞定_第14张图片

你可能感兴趣的:(mybatis,java,spring,boot,mysql,spring)