随着 Mybatis-plus 的兴起,越来越多小伙伴开始接触和使用这一【偷懒神器】,谁用谁知道,操作单表sql使用条件构造器简直不要太爽!

但是近期在好几个 "上班划水群" 看到这样的提问:"请问有人用mybaits-plus操作过多表查询吗?"、"请问mybaits-plus关联表查询条件构造器怎么用啊?"、"mybaits-plus 好像不支持多表条件查询吧?"

针对这一问题,下面做一个简单的学习记录与分享,如果你也有这样的疑问,那么,借我5分钟的时间,看下去吧。

1、首先我们看mp的条件构造器帮我们做了什么:

举例一个最简单的条件查询:
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username", "zyf");
List<User> list = userService.list(queryWrapper);
看控制台执行的sql日志:

image

可以看到最后执行sql语句就是:
SELECT id,username,password FROM user WHERE username = "zyf";
所以 **Wrapper 就是在sql语句后面拼接了询条件**,那么单表设置查询条件很简单,QueryWrapper一直点就是了,那么多表呢?

2、看官网:

其实针对这个问题,[MP官网](https://mp.baomidou.com/guide "MP官网")就给出了答案,就在条件构造器这一栏的最下面,藏的挺深(doge)

image

大概的意思就是**将条件构造器 Wrapper  作为一个参数 传入xml 文件**。

现在再回到问题的关键:【操作多表】,作为一个CRUD工程师,遇到操作多表的需求,第一时间应该想到的就是在xml文件里面写sql语句将表关联起来,而mybaits-plus "刚好" 支持将条件构造器作为参数传入xml。看到这里是不是已经有思路了呢,让我们来验证一下吧。

3、准备数据库结构

表结构:

image

sql脚本:
/*
 Navicat Premium Data Transfer

 Source Server Type    : MySQL
 Source Server Version : 80018
 Source Schema         : blog_demo

 Target Server Type    : MySQL
 Target Server Version : 80018
 File Encoding         : 65001

 Date: 11/08/2021 23:27:08
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` bigint(19) NOT NULL AUTO_INCREMENT COMMENT ' ',
  `username` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用户名',
  `password` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '密码',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'zyf', '123456');
INSERT INTO `user` VALUES (2, 'admin', 'root');
INSERT INTO `user` VALUES (3, 'test', '123456');
INSERT INTO `user` VALUES (4, 'zhangsan', '123456');
INSERT INTO `user` VALUES (5, 'lisi', '123456');
INSERT INTO `user` VALUES (6, 'wangwu', '123456');

-- ----------------------------
-- Table structure for user_info
-- ----------------------------
DROP TABLE IF EXISTS `user_info`;
CREATE TABLE `user_info`  (
  `id` bigint(19) NOT NULL AUTO_INCREMENT,
  `name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '姓名',
  `gender` int(2) UNSIGNED NULL DEFAULT 0 COMMENT '性别:0-男,1-女',
  `age` int(3) NULL DEFAULT 0 COMMENT '年龄',
  `user_id` bigint(19) NULL DEFAULT NULL COMMENT 'user表id',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user_info
-- ----------------------------
INSERT INTO `user_info` VALUES (1, '奇怪的阿峰', 0, 22, 1);
INSERT INTO `user_info` VALUES (2, '管理员', 0, 40, 2);
INSERT INTO `user_info` VALUES (3, '测试账号', 0, 21, 3);
INSERT INTO `user_info` VALUES (4, '张三', 1, 22, 4);
INSERT INTO `user_info` VALUES (5, '李四', 1, 28, 5);
INSERT INTO `user_info` VALUES (6, '王五', 0, 19, 6);

SET FOREIGN_KEY_CHECKS = 1;

4、创建一个简单的SpringBoot

整体结构:

image

pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zyf</groupId>
    <artifactId>blog-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
        <relativePath/>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--mybatisPlus依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.1</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.1</version>
        </dependency>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.15</version>
        </dependency>
        <!--druid依赖-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.22</version>
        </dependency>

        <!--lombok用来简化实体类:需要安装lombok插件-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>
yml文件:
server:
  port: 8008

spring:
  application:
    name: @artifactId@

  mybatis-plus:
    configuration:
      log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
      mapper-locations: classpath:com/zyf/blog/mapper/xml/*.xml
    global-config:
      db-config:
        logic-delete-value: 1
        logic-not-delete-value: 0

  datasource:
    #    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: ##这里换成自己的数据库url
    username: #
    password: #

  hikari:
    connection-test-query: SELECT 1
    connection-timeout: 600000
    idle-timeout: 5000000
    max-lifetime: 5400000
    maximum-pool-size: 12
    minimum-idle: 10
    pool-name: GuliHikariPool

  #配置mapper xml文件的路径
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    auto-mapping-behavior: full
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath*:mapper/**/*Mapper.xml

MyBatisPlusConfig:
@Configuration
@MapperScan("com.zyf.blog.mapper")
public class MyBatisPlusConfig {
	//这个类主要就是用来扫描mapper的,mp提供的插件一般都配置在这里面。
    //也可以不写这个类,直接将@MapperScan("com.zyf.blog.mapper") 注解加到启动类上
}
controller、entity、service、mapper都是生成器生成的,这里就不贴代码了,需要完整代码的小伙伴欢迎访问[鄙人的github](https://github.com/zyf1192306713/mp_multiple_table_joins_query "鄙人的github"),如果对你有帮助的话请顺手帮我点个star哦。

5、模拟需求:查询所有年龄大于20岁的男性用户

查询sql:
基于上面的表结构,这里需要连接user表与user_info表
SELECT
	u.id AS userId,
	u.`password`,
	ui.`name`,
	ui.gender,
	ui.age 
FROM
	`user` u
	LEFT JOIN user_info ui ON u.id = ui.user_id 
WHERE
	ui.gender = 0 
	AND ui.age > 20
sql执行结果:

image

6、代码实现:

创建UserVO对象
将该对象作为返回给前端的数据模型, 包含user表与user_info表的所有属性
@Data
public class UserVO {

    /** 用户id */
    private Long userId;

    /** 用户名 */
    private String username;

    /** 密码 */
    private String password;

    /** 姓名 */
    private String name;

    /** 性别:0-男,1-女 */
    private Integer gender;

    /** 年龄 */
    private Integer age;
}
UserController添加一个查询接口:
/**
 * @Author zyf
 * @Date 2021/8/11 21:56
 * @Version 1.0
 */
@CrossOrigin
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/list")
    public List<UserVO> list() {
        List<UserVO> list = userService.findList();
        return list;
    }
}
UserService:
/**
 * @Author zyf
 * @Date 2021/8/11 22:05
 * @Version 1.0
 */
public interface UserService extends IService<User> {

    List<UserVO> findList();
}
UserServiceImpl:
/**
 * @Author zyf
 * @Date 2021/8/11 22:06
 * @Version 1.0
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Override
    public List<UserVO> findList() {

        //注意 这里不能使用LambdaQueryWrapper 为啥?因为LambdaQueryWrapper接收不了 ui.gender 这种参数
        QueryWrapper<UserVO> queryWrapper = new QueryWrapper<>();
        //没想到吧!这里可以直接传 表别名.字段
        queryWrapper.eq("ui.gender", 0)
                .gt("ui.age", 20);

        List<UserVO> list = baseMapper.selectListByQuery(queryWrapper);
        return list;
    }
}
UserMapper:
/**
 * @Author zyf
 * @Date 2021/8/11 22:06
 * @Version 1.0
 */
public interface UserMapper extends BaseMapper<User> {

    //这里注意 @Param(Constants.WRAPPER) 这个参数注解别丢了,否则xml文件里面解析不了会报错
    List<UserVO> selectListByQuery(@Param(Constants.WRAPPER) QueryWrapper<UserVO> queryWrapper);
}
UserMapepr.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="com.zyf.blog.mapper.UserMapper">
    <select id="selectListByQuery" resultType="com.zyf.blog.entity.UserVO">
        SELECT
            u.id AS userId,
            u.`password`,
            ui.`name`,
            ui.gender,
            ui.age
        FROM
            `user` u
            LEFT JOIN user_info ui ON u.id = ui.user_id

        <!-- 这就是刚刚注解传进来的条件构造器 -->
        ${ew.customSqlSegment}
    </select>
</mapper>

7、功能测试

调用接口,查看返回结果:

image

再看控制台sql日志:
果然,成功将Wrapper设置的查询条件拼接到sql语句后面了。

image

大功告成,如果这篇文章对你有帮助的话请顺手帮忙点个赞,建议收藏一下,下次要复制粘贴的时候就不会找不到了^_^。