Mybatis-plus

目录

一、MyBatis-Plus简介

1、简介

2、特性

3、支持数据库

4、框架结构

 5、代码及文档地址

二、入门案例

1、创建数据库及表

a>创建表

b>添加数据

3、创建Spring Boot工程

a>初始化工程

b>引入依赖

4、编写代码

a>配置application.yml

b>启动类

c>添加实体

d>添加mapper

e>测试

 f>添加日志

三、基本CRUD

1、BaseMapper

 2、插入

3、删除

a>通过id删除记录

b>通过id批量删除记录

c>通过map条件删除记录

4、修改

5、查询

b>根据多个id查询多个用户信息

c>通过map条件查询用户信息

d>查询所有数据

6、自定义功能

结果:

7、通用Service

a>IService

b>创建Service接口和实现类

c>测试查询记录数

d>测试批量插入

四、常用注解

1、@TableName

a>问题

 b>通过@TableName解决问题

c>通过全局配置解决问题

2、@TableId

@TableId的value属性

@TableId的type属性

e>雪花算法

3、@TableField 

a>情况1

b>情况2

4、@TableLogic

a>逻辑删除

b>实现逻辑删除

五、条件构造器和常用接口

1、wapper介绍

e>例5:组装select子句

2、QueryWrapper

a>例1:组装查询条件

b>例2:组装排序条件

c>例3:组装删除条件

d>例4:条件的优先级

e>例5:组装select子句

f>例6:实现子查询

3、UpdateWrapper

4、condition

5、LambdaQueryWrapper

6、LambdaUpdateWrapper

六、插件

1、分页插件

a>添加配置类

b>测试

2、xml自定义分页

a>UserMapper中定义接口方法

b>UserMapper.xml中编写SQL

c>测试

3、乐观锁

a>场景

b>乐观锁与悲观锁

c>模拟修改冲突

d>乐观锁实现流程 

七、通用枚举

a>数据库表添加字段sex

b>创建通用枚举类型 

c>配置扫描通用枚举

d>测试

八、代码生成器

1、引入依赖

2、快速生成

 九、多数据源

1、创建数据库及表

2、引入依赖 

3、配置多数据源

4、创建用户service

5、创建商品service

6、测试

十、MyBatisX插件

1.生成代码

1.安装

2. 快速生成CRUD


一、MyBatis-Plus简介

1、简介

MyBatis-Plus (简称 MP )是一个 MyBatis 的增强工具 ,在 MyBatis 的基础上 只做增强不做改变 ,为
简化开发、提高效率而生
愿景
我们的愿景是成为 MyBatis 最好的搭档,就像魂斗罗中的 1P 2P ,基友搭配,效率翻倍。

2、特性

无侵入 :只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
损耗小 :启动即会自动注入基本 CURD ,性能基本无损耗,直接面向对象操作
强大的 CRUD 操作 :内置通用 Mapper 、通用 Service ,仅仅通过少量配置即可实现单表大部分
CRUD 操作,更有强大的条件构造器,满足各类使用需求
支持 Lambda 形式调用 :通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
支持主键自动生成 :支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence ),可自由
配置,完美解决主键问题
支持 ActiveRecord 模式 :支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强
大的 CRUD 操作
支持自定义全局通用操作 :支持全局通用方法注入( Write once, use anywhere
内置代码生成器 :采用代码或者 Maven 插件可快速生成 Mapper Model Service
Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
内置分页插件 :基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等
同于普通 List 查询
分页插件支持多种数据库 :支持 MySQL MariaDB Oracle DB2 H2 HSQL SQLite
Postgre SQLServer 等多种数据库
内置性能分析插件 :可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出
慢查询
内置全局拦截插件 :提供全表 delete update 操作智能分析阻断,也可自定义拦截规则,预防
误操作

3、支持数据库

任何能使用 MyBatis 进行 CRUD, 并且支持标准 SQL 的数据库,具体支持情况如下
MySQL Oracle DB2 H2 HSQL SQLite PostgreSQL SQLServer Phoenix Gauss
ClickHouse Sybase OceanBase Firebird Cubrid Goldilocks csiidb
达梦数据库,虚谷数据库,人大金仓数据库,南大通用 ( 华库 ) 数据库,南大通用数据库,神通数据
库,瀚高数据库

4、框架结构

 5、代码及文档地址

官方地址 : http://mp.baomidou.com
代码发布地址 :
Github: https://github.com/baomidou/mybatis-plus
Gitee: https://gitee.com/baomidou/mybatis-plus
文档发布地址 : https://baomidou.com/pages/24112f

二、入门案例

1、创建数据库及表

a>创建表

CREATE DATABASE `mybatis_plus` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;
use `mybatis_plus`;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`name` varchar(30) DEFAULT NULL COMMENT '姓名',
`age` int(11) DEFAULT NULL COMMENT '年龄',
`email` varchar(50) DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

b>添加数据


INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');

3、创建Spring Boot工程

a>初始化工程

使用 Spring Initializr 快速初始化一个 Spring Boot 工程

b>引入依赖

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

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
<!--        mybatis-plus的启动器-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>
<!--        lombok用于简化实体类的开发-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
<!--        mysql的驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

    </dependencies>

4、编写代码


a>配置application.yml

spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=UTC
    username: root
    password: woaini520

b>启动类

@SpringBootApplication
//扫描mapper接口所在的包
@MapperScan("com.javastudy.mybatisplus.mapper")
public class Mybatisplus01Application {

    public static void main(String[] args) {
        SpringApplication.run(Mybatisplus01Application.class, args);
    }

}

c>添加实体

@Data
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

d>添加mapper

@Repository
public interface UserMapper extends BaseMapper<User> {
}

e>测试

@SpringBootTest
public class MybatisPlusTest {
    @Autowired
    UserMapper mapper;

    @Test
    public void testSelectList(){
        //通过条件构造器查询一个list集合,若没有条件,可以设置null
        List<User> users = mapper.selectList(null);
        for (User user : users) {
            System.out.println(user);
        }


    }

}

结果

 f>添加日志

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

三、基本CRUD

1BaseMapper

MyBatis-Plus 中的基本 CRUD 在内置的 BaseMapper 中都已得到了实现,我们可以直接使用,接口如 下:

 2、插入

 @Test
    /**
     * 新增用户信息
     */
    public void testInsert() {
        //通过条件构造器查询一个list集合,若没有条件,可以设置null
        User user = new User();
        user.setName("张选宁");
        user.setAge(19);
        user.setEmail("15641554@qq.com");
        System.out.println("id:" + user.getId());
        //INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
        int insert = mapper.insert(user);
        System.out.println("产生条数:" + insert);
        System.out.println("id:" + user.getId());


    }

3、删除

a>通过id删除记录

 @Test
    public void testDeleteById() { 
        //通过id删除用户信息 
        // DELETE FROM user WHERE id=? 
        int result = mapper.deleteById(1475754982694199298L);
        System.out.println("受影响行数:" + result);
    }

b>通过id批量删除记录

  @Test
    public void testDeleteBatchIds(){
//通过多个id批量删除
//DELETE FROM user WHERE id IN ( ? , ? , ? )
        List<Long> idList = Arrays.asList(1L, 2L, 3L);
        int result = mapper.deleteBatchIds(idList);
        System.out.println("受影响行数:"+result);
    }

c>通过map条件删除记录

   @Test
    public void testDeleteByMap() {
//根据map集合中所设置的条件删除记录
//DELETE FROM user WHERE name = ? AND age = ?
        Map<String, Object> map = new HashMap<>();
        map.put("age", 23);
        map.put("name", "张三");
        int result = mapper.deleteByMap(map);
        System.out.println("受影响行数:" + result);
    }

4、修改

  @Test
    public void testUpdateById(){
        User user = new User(4L, "admin", 22, null);
//UPDATE user SET name=?, age=? WHERE id=?
        int result = mapper.updateById(user);
        }

5、查询

a>根据id查询用户信息

  @Test
    public void testSelectBatchIds(){
//根据多个id查询多个用户信息
//SELECT id,name,age,email FROM user WHERE id IN ( ? , ? )
        List<Long> idList = Arrays.asList(4L, 5L);
        List<User> list = mapper.selectBatchIds(idList);
        list.forEach(System.out::println);
    }

b>根据多个id查询多个用户信息

   @Test
    public void testSelectById(){
//根据id查询用户信息
//SELECT id,name,age,email FROM user WHERE id=?
        User user = mapper.selectById(4L);
        System.out.println(user);
    }

c>通过map条件查询用户信息

    @Test
    public void testSelectByMap(){
//通过map条件查询用户信息
//SELECT id,name,age,email FROM user WHERE name = ? AND age = ?
        Map<String, Object> map = new HashMap<>();
        map.put("age", 22);
        map.put("name", "admin");
        List<User> list = mapper.selectByMap(map);
        list.forEach(System.out::println);
    }

d>查询所有数据

    @Test
    public void testSelectList(){
//查询所有用户信息
//SELECT id,name,age,email FROM user
        List<User> list = mapper.selectList(null);
        list.forEach(System.out::println);
    }

6、自定义功能

 

    /**
     * 根据id查询用户信息为Mapper集合
     * @param id
     * @return
     */
    Map<String,Object> selectMapById(Long id);
<?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.javastudy.mybatisplus.mapper.UserMapper">

    <select id="selectMapById" resultType="map">
        select id,name,age,email from user where id=#{id};
    </select>


</mapper>
  @Test
    /**
     * 测试查询功能
     */
    public void testSelect() {
        Map<String, Object> map = mapper.selectMapById(1L);
        System.out.println(map);

    }

结果:

{name=张选宁, id=1, age=19, email=147450@qq.com}

7、通用Service

说明 :
通用 Service CRUD 封装 IService 接口,进一步封装 CRUD 采用 get 查询单行 remove
list 查询集合 page 分页 前缀命名方式区分 Mapper 层避免混淆,
泛型 T 为任意实体对象
建议如果存在自定义通用 Service 方法的可能,请创建自己的 IBaseService 继承
Mybatis - Plus 提供的基类
官网地址: https://baomidou.com/pages/49cc81/#service-crud-%E6%8E%A5%E5%8F%
A3

a>IService

MyBatis-Plus 中有一个接口 IService 和其实现类 ServiceImpl ,封装了常见的业务层逻辑
详情查看源码 IService ServiceImpl

b>创建Service接口和实现类

public interface UserService extends IService<User> {

}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

}

c>测试查询记录数

    @Test
    public void testGetCount(){
        //SELECT COUNT( * ) FROM user
        long count = service.count();
        System.out.println("总数量:"+count);
    }

d>测试批量插入

    @Test
    public void testInsertMore(){
        List<User> users = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            User user = new User();
            user.setName("zxn"+i);
            user.setAge(20+i);
            user.setEmail("147450@qq.com");
            users.add(user);
        }
        //INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
        boolean b = service.saveBatch(users);
        System.out.println(b);

    }

四、常用注解

1@TableName

经过以上的测试,在使用 MyBatis-Plus 实现基本的 CRUD 时,我们并没有指定要操作的表,只是在
Mapper 接口继承 BaseMapper 时,设置了泛型 User ,而操作的表为 user
由此得出结论,MyBatis-Plus在确定操作的表时,由 BaseMapper 的泛型决定,即实体类型决
定,且默认操作的表名和实体类型的类名一致

a>问题

当表名和实体类的类名不一致时,会出现以下问题

 b>通过@TableName解决问题

@Data
//设置实体类所对应的表名
@TableName("t_user")
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}
SQL语句发生对应的改变

c>通过全局配置解决问题

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    #设置mybatisplus的全局配置,设置实体类所对应表的统一前缀
  global-config:
    db-config:
      table-prefix: t_

2@TableId

当作为主键的字段的名字不为id时,加入@TableId将其变为主键

   //将属性所对应的字段指定为主键
    @TableId
    private Long id;

@TableIdvalue属性

当实体类的属性为id,而字段名为uid(比如),可以用 @TableId value 属性,来进行一一对应
 @TableId(value = "uid")
    private Long id;

@TableIdtype属性

默认的为雪花算法

描述
IdType.ASSIGN_ID (默
认)
基于雪花算法的策略生成数据 id ,与数据库 id 是否设置自增无关
IdType.AUTO
使用数据库的自增策略,注意,该类型请确保数据库设置了 id 自增,
否则无效

改变为主键自增

    //@TableId注解的type属性设置主键生成策略
    @TableId(value = "uid",type = IdType.AUTO)
    private Long id;

注意:只在数据库里面将字段设置为主键自增是没有效果的

当我们自己设置了id的值,insert添加数据到表中,是不会用雪花算法的

配置全局主键策略:
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    #设置mybatisplus的全局配置,设置实体类所对应表的统一前缀
  global-config:
    db-config:
      table-prefix: t_
      #设置统一的主键生成策略
      id-type: auto

e>雪花算法

背景
需要选择合适的方案去应对数据规模的增长,以应对逐渐增长的访问压力和数据量。
数据库的扩展方式主要包括:业务分库、主从复制,数据库分表。
数据库分表
将不同业务数据分散存储到不同的数据库服务器,能够支撑百万甚至千万用户规模的业务,但如果业务 继续发展,同一业务的单表数据也会达到单台数据库服务器的处理瓶颈。例如,淘宝的几亿用户数据, 如果全部存放在一台数据库服务器的一张表中,肯定是无法满足性能要求的,此时就需要对单表数据进 行拆分。
单表数据拆分有两种方式:垂直分表和水平分表。示意图如下:

垂直分表
垂直分表适合将表中某些不常用且占了大量空间的列拆分出去。
例如,前面示意图中的 nickname description 字段,假设我们是一个婚恋网站,用户在筛选其他用 户的时候,主要是用 age sex 两个字段进行查询,而 nickname description 两个字段主要用于展 示,一般不会在业务查询中用到。description 本身又比较长,因此我们可以将这两个字段独立到另外 一张表中,这样在查询 age sex 时,就能带来一定的性能提升。
水平分表
水平分表适合表行数特别大的表,有的公司要求单表行数超过 5000 万就必须进行分表,这个数字可以 作为参考,但并不是绝对标准,关键还是要看表的访问性能。对于一些比较复杂的表,可能超过 1000 万就要分表了;而对于一些简单的表,即使存储数据超过 1 亿行,也可以不分表。
但不管怎样,当看到表的数据量达到千万级别时,作为架构师就要警觉起来,因为这很可能是架构的性 能瓶颈或者隐患。
水平分表相比垂直分表,会引入更多的复杂性,例如要求全局唯一的数据 id 该如何处理
主键自增
①以最常见的用户 ID 为例,可以按照 1000000 的范围大小进行分段, 1 ~ 999999 放到表 1 中,
1000000 ~ 1999999 放到表 2 中,以此类推。
②复杂点:分段大小的选取。分段太小会导致切分后子表数量过多,增加维护复杂度;分段太大可能会导致单表依然存在性能问题,一般建议分段大小在 100 万至 2000 万之间,具体需要根据业务选取合适 的分段大小。
③优点:可以随着数据的增加平滑地扩充新的表。例如,现在的用户是 100 万,如果增加到 1000 万, 只需要增加新的表就可以了,原有的数据不需要动。
④缺点:分布不均匀。假如按照 1000 万来进行分表,有可能某个分段实际存储的数据量只有 1 条,而另外一个分段实际存储的数据量有 1000 万条。
取模
①同样以用户 ID 为例,假如我们一开始就规划了 10 个数据库表,可以简单地用 user_id % 10 的值来
表示数据所属的数据库表编号, ID 985 的用户放到编号为 5 的子表中, ID 10086 的用户放到编号 为 6 的子表中。
②复杂点:初始表数量的确定。表数量太多维护比较麻烦,表数量太少又可能导致单表性能存在问题。
③优点:表分布比较均匀。
④缺点:扩充新的表很麻烦,所有数据都要重分布。
雪花算法
雪花算法是由 Twitter 公布的分布式主键生成算法,它能够保证不同表的主键的不重复性,以及相同表的
主键的有序性。
①核心思想:
长度共 64bit (一个 long 型)。
首先是一个符号位, 1bit 标识,由于 long 基本类型在 Java 中是带符号的,最高位是符号位,正数是 0 ,负数是1 ,所以 id 一般是正数,最高位是 0
41bit 时间截 ( 毫秒级 ) ,存储的是时间截的差值(当前时间截 - 开始时间截 ) ,结果约等于 69.73 年。
10bit 作为机器的 ID 5 bit 是数据中心, 5 bit 的机器 ID ,可以部署在 1024 个节点)。
12bit 作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 ID )。

②优点:整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞,并且效率较高。

3@TableField 

经过以上的测试,我们可以发现, MyBatis-Plus 在执行 SQL 语句时,要保证实体类中的属性名和
表中的字段名一致
如果实体类中的属性名和字段名不一致的情况,会出现什么问题呢?

a>情况1

若实体类中的属性使用的是驼峰命名风格,而表中的字段使用的是下划线命名风格
例如实体类属性 userName ,表中字段 user_name
此时 MyBatis-Plus 会自动将下划线命名风格转化为驼峰命名风格
相当于在 MyBatis 中配置以下:
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

b>情况2

若实体类中的属性和表中的字段不满足情况 1
例如实体类属性 name ,表中字段 username
此时需要在实体类属性上使用 @TableField("username") 设置属性所对应的字段名
 //指定属性所对应的字段名
    @TableField("user_name")
    private String name;

4@TableLogic

a>逻辑删除

物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除的数据
逻辑删除:假删除,将对应数据中代表是否被删除字段的状态修改为 被删除状态 ,之后在数据库
中仍旧能看到此条数据记录
使用场景:可以进行数据恢复

b>实现逻辑删除

在表中添加字段is_delete 

实体类中添加属性,并加上@TableLogic注解

@TableLogic
private int isDelete;

之后的删除操作变为

UPDATE t_user SET is_delete=1 WHERE uid IN ( ? , ? , ? ) AND is_delete=0

原本

删除之后变为

 查找的sql语句变为:

SELECT uid AS id,user_name AS name,age,email,is_delete FROM t_user WHERE is_delete=0

 便查不到is_delete为1的数据了

五、条件构造器和常用接口

1wapper介绍

Wrapper : 条件构造抽象类,最顶端父类
    AbstractWrapper : 用于查询条件封装,生成 sql where 条件
         QueryWrapper : 查询条件封装
         UpdateWrapper Update 条件封装
          AbstractLambdaWrapper : 使用 Lambda 语法
                LambdaQueryWrapper :用于Lambda 语法使用的查询 Wrapper
                LambdaUpdateWrapper : Lambda 更新封装 Wrapper

e>5:组装select子句

2QueryWrapper

a>1:组装查询条件

   @Test
    public void test01(){
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        QueryWrapper<User> queryWrapper = wrapper.like("user_name", "宁").
                between("age", 15, 30).isNotNull("email");
        //SELECT uid AS id,user_name AS name,age,email,is_delete FROM t_user
        // WHERE is_delete=0 AND (user_name LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
        List<User> users = mapper.selectList(queryWrapper);
        for (User user : users) {
            System.out.println(user);
        }


    }

b>2:组装排序条件

    @Test
    public void test02(){
        //查询用户信息按照年龄的降序排序,若年龄相同,则按照id升序排序
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        QueryWrapper<User> userQueryWrapper = wrapper.orderByDesc("age").orderByAsc("uid");
        //SELECT uid AS id,user_name AS name,age,email,is_delete FROM t_user 
        // WHERE is_delete=0 ORDER BY age DESC,uid ASC
        List<User> users = mapper.selectList(userQueryWrapper);
        for (User user : users) {
            System.out.println(user);
        }


    }

c>3:组装删除条件

   @Test
    public void test03(){
        //删除邮箱的地址为null的用户信息
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        QueryWrapper<User> queryWrapper = wrapper.isNull("email");
        //UPDATE t_user SET is_delete=1 WHERE is_delete=0 AND (email IS NULL)
        int delete = mapper.delete(queryWrapper);
        System.out.println("删除记录数为:"+delete);


    }

d>4:条件的优先级

    @Test
    public void test04(){
        //将(年龄大于20并且用户名中包含有a)或邮箱为null的用户信息修改
        UpdateWrapper<User> wrapper = new UpdateWrapper<>();

        UpdateWrapper<User> updateWrapper = wrapper.gt("age", 20).like("user_name", "宁")
                .or().isNull("email");
        User user = new User();
        user.setName("小宁子");
        user.setAge(18);
        //UPDATE t_user SET user_name=?, age=? WHERE is_delete=0 AND (age > ? AND user_name LIKE ? OR email IS NULL)
        int update = mapper.update(user, updateWrapper);
        System.out.println("更新了条数:"+update);


    }
    @Test
    public void test05(){
        //将用户名中包含有"宁"并且(年龄大于20或邮箱为null)的用户信息修改
        UpdateWrapper<User> wrapper = new UpdateWrapper<>();

        UpdateWrapper<User> updateWrapper = wrapper.like("user_name", "宁").
                and(i -> i.gt("age", 20).or().isNull("email"));
        User user = new User();
        user.setName("张选宁");
        user.setAge(20);
        //UPDATE t_user SET user_name=?, age=?
        // WHERE is_delete=0 AND (user_name LIKE ? AND (age > ? OR email IS NULL))
        int update = mapper.update(user, updateWrapper);
        System.out.println("更新了条数:"+update);



    }

e>5:组装select子句

    @Test
    public void test06(){
        //查询用户的用户名、年龄、邮箱信息
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        QueryWrapper<User> select = wrapper.select("user_name", "age", "email");


        List<Map<String, Object>> maps = mapper.selectMaps(select);

        for (Map<String, Object> map : maps) {
            System.out.println(map);
        }


    }

f>6:实现子查询

    @Test
    public void test07(){
       //查询id小于等于100的用户信息
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        QueryWrapper<User> queryWrapper = wrapper.inSql("uid", "select uid from t_user where uid<=100");
        //SELECT uid AS id,user_name AS name,age,email,is_delete FROM t_user 
        // WHERE is_delete=0 AND (uid IN (select uid from t_user where uid<=100))
        List<User> users = mapper.selectList(queryWrapper);
        for (User user : users) {
            System.out.println(user);
        }

    }

3UpdateWrapper

    @Test
    public void test08(){
        //将用户名中包含有"宁"并且(年龄大于20或邮箱为null)的用户信息修改
        UpdateWrapper<User> wrapper = new UpdateWrapper<>();

        UpdateWrapper<User> updateWrapper = wrapper.like("user_name", "宁").
                and(i -> i.gt("age", 20).or().isNull("email"));
        UpdateWrapper<User> updateWrapper1 = updateWrapper.set("user_name", "刘子").set("age", 19);

        //UPDATE t_user SET user_name=?,age=?
        // WHERE is_delete=0 AND (user_name LIKE ? AND (age > ? OR email IS NULL))
        int update = mapper.update(null,updateWrapper1);
        System.out.println("更新了条数:"+update);


    }

4condition

模拟开发中组装条件的情况

    @Test
    public void test09(){
        String username="";
        Integer ageBegin=20;
        Integer ageEnd=30;
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        if(StringUtils.isNotBlank(username)){
           wrapper.like("user_name", username);
        }
        if(ageBegin!=null){
            wrapper.ge("age",ageBegin);
        }
        if(ageEnd!=null){
            wrapper.le("age",ageEnd);
        }
         //SELECT uid AS id,user_name AS name,age,email,is_delete FROM t_user
        // WHERE is_delete=0 AND (age >= ? AND age <= ?)
        List<Map<String, Object>> maps = mapper.selectMaps(wrapper);
        for (Map<String, Object> map : maps) {
            System.out.println(map);
        }


    }

使用condition组装条件   ----------->推荐

    @Test
    public void test10(){
        String username="";
        Integer ageBegin=20;
        Integer ageEnd=30;
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.like(StringUtils.isNotBlank(username),"user_name",username);
        wrapper.ge(ageBegin!=null,"age",ageBegin);
        wrapper.le(ageEnd!=null,"age",ageEnd);

        List<User> users = mapper.selectList(wrapper);
        for (User user : users) {
            System.out.println(user);
        }


    }

5LambdaQueryWrapper

    @Test
    public void test11(){
        String username="";
        Integer ageBegin=20;
        Integer ageEnd=30;
        LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userLambdaQueryWrapper.like(StringUtils.isNotBlank(username),User::getName,username);
        userLambdaQueryWrapper.ge(ageBegin!=null,User::getAge,ageBegin);
        userLambdaQueryWrapper.ge(ageEnd!=null,User::getAge,ageEnd);

        List<User> users = mapper.selectList(userLambdaQueryWrapper);
        for (User user : users) {
            System.out.println(user);
        }


    }

6LambdaUpdateWrapper


    @Test
    public void test12(){
        //将用户名中包含有"宁"并且(年龄大于20或邮箱为null)的用户信息修改
        LambdaUpdateWrapper<User> userLambdaUpdateWrapper = new LambdaUpdateWrapper<>();

        LambdaUpdateWrapper<User> updateWrapper = userLambdaUpdateWrapper.like(User::getName, "刘").
                and(i -> i.gt(User::getAge, 20).or().isNull(User::getEmail));

        LambdaUpdateWrapper<User> updateWrapper1 = updateWrapper.set(User::getName, "刘子").set(User::getAge, 21);

        //UPDATE t_user SET user_name=?,age=?
        // WHERE is_delete=0 AND (user_name LIKE ? AND (age > ? OR email IS NULL))
        int update = mapper.update(null,updateWrapper1);
        System.out.println("更新了条数:"+update);



    }

六、插件

1、分页插件

MyBatis Plus 自带分页插件,只要简单的配置即可实现分页功能

a>添加配置类

@Configuration
//扫描mapper接口所在的包
@MapperScan("com.javastudy.mybatisplus.mapper")
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;


    }

}

b>测试

 @Test
    public void  testPage(){
        Page<User> userPage = new Page<>(3,3);
        //SELECT uid AS id,user_name AS name,age,email,is_delete FROM t_user WHERE is_delete=0 LIMIT ?,?
        Page<User> userPage1 = mapper.selectPage(userPage, null);
        System.out.println(userPage1.getRecords());
        //[User(id=7, name=小宁子, age=18, email=15641554@qq.com, isDelete=0),
        // User(id=8, name=刘子, age=19, email=15641554@qq.com, isDelete=0),
        // User(id=9, name=刘子, age=21, email=null, isDelete=0)]
        System.out.println(userPage1.getPages());//4
        System.out.println(userPage1.getTotal());//10
        System.out.println(userPage1.hasNext());//true
        System.out.println(userPage1.hasPrevious());//true
    }

2xml自定义分页

a>UserMapper中定义接口方法

 Page<User> selectVo(@Param("page") Page<User> page,@Param("age") Integer age);

b>UserMapper.xml中编写SQL

    <select id="selectVo" resultType="user">
        select * from t_user where age>#{age}
    </select>

c>测试

    @Test
    public void testVo(){
        Page<User> userPage = new Page<>(1,3);
        Page<User> userPage1 = mapper.selectVo(userPage, 20);
        List<User> records = userPage1.getRecords();
        System.out.println(records);

    }

结果

 Preparing: select * from t_user where age>? LIMIT ?
==> Parameters: 20(Integer), 3(Long)
<==    Columns: uid, user_name, age, email, is_delete
<==        Row: 3, 乔浪, 26, 147450@qq.com, 0
<==        Row: 4, 李四, 21, 147450@qq.com, 0
<==        Row: 5, Billie, 24, test5@baomidou.com, 0
<==      Total: 3

3、乐观锁

a>场景

一件商品,成本价是 80 元,售价是 100 元。老板先是通知小李,说你去把商品价格增加 50 元。小
李正在玩游戏,耽搁了一个小时。正好一个小时后,老板觉得商品价格增加到 150 元,价格太
高,可能会影响销量。又通知小王,你把商品价格降低 30 元。
此时,小李和小王同时操作商品后台系统。小李操作的时候,系统先取出商品价格 100 元;小王
也在操作,取出的商品价格也是 100 元。小李将价格加了 50 元,并将 100+50=150 元存入了数据
库;小王将商品减了 30 元,并将 100-30=70 元存入了数据库。是的,如果没有锁,小李的操作就
完全被小王的覆盖了。
现在商品价格是 70 元,比成本价低 10 元。几分钟后,这个商品很快出售了 1 千多件商品,老板亏 1
万多。

b>乐观锁与悲观锁

上面的故事,如果是乐观锁,小王保存价格前,会检查下价格是否被人修改过了。如果被修改过
了,则重新取出的被修改后的价格, 150 元,这样他会将 120 元存入数据库。
如果是悲观锁,小李取出数据后,小王只能等小李操作完之后,才能对价格进行操作,也会保证
最终的价格是 120 元。

c>模拟修改冲突

数据库中增加商品表
CREATE TABLE t_product
(
id BIGINT ( 20 ) NOT NULL COMMENT ' 主键 ID' ,
NAME VARCHAR ( 30 ) NULL DEFAULT NULL COMMENT ' 商品名称 ' ,
price INT ( 11 ) DEFAULT 0 COMMENT ' 价格 ' ,
VERSION INT ( 11 ) DEFAULT 0 COMMENT ' 乐观锁版本号 ' ,
PRIMARY KEY (id)
);

添加数据

INSERT INTO t_product (id, NAME, price) VALUES (1, '外星人笔记本', 100);

添加实体

@Data
public class Product {
    private Long id;
    private String name;
    private Integer price;
    private Integer version;
}

添加mapper

@Repository
public interface ProductMapper extends BaseMapper<Product> {
}

测试 

  @Test
    public void test11(){
        //小李查询商品价格
        Product productLi = productMapper.selectById(1);
        System.out.println("小李查询到的商品:"+productLi);
        //小王查询到的价格
        Product productWang = productMapper.selectById(1);
        System.out.println("小王查询到的商品:"+productWang);
        //小李将商品价格加50
        productLi.setPrice(productLi.getPrice()+50);
        productMapper.updateById(productLi);
        //小王将商品价格减30
        productWang.setPrice(productWang.getPrice()-30);
        productMapper.updateById(productWang);
        //老板查询价格
        Product productBoos = productMapper.selectById(1);
        System.out.println("老板查询到的商品:"+productBoos);



    }

最后产生的结果

老板查询到的商品:Product(id=1, name=外星人笔记本, price=70, version=0)

d>乐观锁实现流程 

修改实体类
@Data
public class Product {
    private Long id;
    private String name;
    private Integer price;
    @Version
    private Integer version;

}
添加乐观锁插件配置
@Configuration
//扫描mapper接口所在的包
@MapperScan("com.javastudy.mybatisplus.mapper")
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //添加分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        //添加乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;


    }

}
优化流程
    @Test
    public void test11(){
        //小李查询商品价格
        Product productLi = productMapper.selectById(1);
        System.out.println("小李查询到的商品:"+productLi);
        //小王查询到的价格
        Product productWang = productMapper.selectById(1);
        System.out.println("小王查询到的商品:"+productWang);
        //小李将商品价格加50
        productLi.setPrice(productLi.getPrice()+50);
        productMapper.updateById(productLi);
        //小王将商品价格减30
        productWang.setPrice(productWang.getPrice()-30);
        int result = productMapper.updateById(productWang);
        while(result==0){
            //操作失败,重试
            Product productWang2 = productMapper.selectById(1);
            System.out.println("小王查询到的商品:"+productWang2);
            productWang2.setPrice(productWang2.getPrice()-30);
            result = productMapper.updateById(productWang2);

        }
        //老板查询价格
        Product productBoos = productMapper.selectById(1);
        System.out.println("老板查询到的商品:"+productBoos);



    }

七、通用枚举

表中的有些字段值是固定的,例如性别(男或女),此时我们可以使用 MyBatis-Plus 的通用枚举
来实现

a>数据库表添加字段sex

b>创建通用枚举类型 

@Getter
public enum SexEnums {
    Male(0,"男"),
    Female(1,"女");

    @EnumValue  //将注解所表示的属性值存储到数据库中
    private Integer sex;
    private String sexName;

    SexEnums(Integer sex, String sexName) {
        this.sex = sex;
        this.sexName = sexName;
    }
}

c>配置扫描通用枚举

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    #设置mybatisplus的全局配置,设置实体类所对应表的统一前缀
  global-config:
    db-config:
      table-prefix: t_
      #设置统一的主键生成策略
      id-type: auto
  #配置类型别名所对应的包
  type-aliases-package: com.javastudy.mybatisplus.entity
  #扫描通用枚举的包
  type-enums-package: com.javastudy.mybatisplus.enums

d>测试

@SpringBootTest
public class MyBatisPlusEnumTest {
    @Autowired
    UserMapper userMapper;
    @Test
    public void test01(){
        User user = new User();
        user.setName("薛子");
        user.setAge(18);
        user.setEmail("147450@qq.com");
        user.setSex(SexEnums.Male);
        int insert = userMapper.insert(user);
        System.out.println("result:"+insert);
    }

}

八、代码生成器

1、引入依赖

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.1</version>
        </dependency>

        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.31</version>
        </dependency>

2、快速生成

public class FastAutoGeneratorTest {
    public static void main(String[] args) {
        FastAutoGenerator.create("jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=UTC",
                        "root", "woaini520")
                .globalConfig(builder -> {
                    builder.author("Chooker") // 设置作者
                            //.enableSwagger() // 开启 swagger 模式
                            .fileOverride() // 覆盖已生成文件
                            .outputDir("D://mybatis_plus"); // 指定输出目录
                })
                .packageConfig(builder -> {
                    builder.parent("com.javastudy") // 设置父包名
                            .moduleName("mybatisplus") // 设置父包模块名
                            .pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D://mybatis_plus"));
                    // 设置mapperXml生成路径
                })
                .strategyConfig(builder -> {
                    builder.addInclude("t_user") // 设置需要生成的表名
                            .addTablePrefix("t_", "c_"); // 设置过滤表前缀
                })
                .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
                .execute();


    }




}

生成的结果

 九、多数据源

适用于多种场景:纯粹多库、 读写分离、 一主多从、 混合模式等
目前我们就来模拟一个纯粹多库的一个场景,其他场景类似
场景说明:
我们创建两个库,分别为: mybatis_plus (以前的库不动)与 mybatis_plus_1 (新建),将
mybatis_plus 库的 product 表移动到 mybatis_plus_1 库,这样每个库一张表,通过一个测试用例
分别获取用户数据与商品数据,如果获取到说明多库模拟成功

1、创建数据库及表

创建数据库 mybatis_plus_1 和表 product
CREATE DATABASE `mybatis_plus_1` /*!40100 DEFAULT CHARACTER SET utf8mb4 */ ;
use `mybatis_plus_1`;
CREATE TABLE product
(
id BIGINT ( 20 ) NOT NULL COMMENT ' 主键 ID' ,
name VARCHAR ( 30 ) NULL DEFAULT NULL COMMENT ' 商品名称 ' ,
price INT ( 11 ) DEFAULT 0 COMMENT ' 价格 ' ,
version INT ( 11 ) DEFAULT 0 COMMENT ' 乐观锁版本号 ' ,
PRIMARY KEY (id)
);
添加测试数据
INSERT INTO product (id, NAME, price) VALUES ( 1 , ' 外星人笔记本 ' , 100 );

删除mybatis_plus库的product 

use mybatis_plus;
DROP TABLE IF EXISTS product;

2、引入依赖 

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
            <version>3.5.0</version>
        </dependency>

3、配置多数据源

说明:注释掉之前的数据库连接,添加新配置
spring:
  datasource:
    dynamic:
      primary: master
      strict: false
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=UTC
          driver-class-name: com.mysql.cj.jdbc.Driver
          username: root
          password: woaini520
        slave_1:
          url: jdbc:mysql://localhost:3306/mybatis_plus_1?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=UTC
          driver-class-name: com.mysql.cj.jdbc.Driver
          username: root
          password: woaini520

4、创建用户service

public interface UserService extends IService<User> {
}
@Service
@DS("master")
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

}

5、创建商品service

public interface ProductService extends IService<Product> {
}
@Service
@DS("slave_1")
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {
}

6、测试

@SpringBootTest
class Mybatisplus02ApplicationTests {

   @Autowired
    UserService userService;
   @Autowired
    ProductService productService;
   @Test
    public void test01(){
       User byId = userService.getById(1L);
       System.out.println(byId);
       Product byId1 = productService.getById(1L);
       System.out.println(byId1);


   }

}

十、MyBatisX插件

MyBatis-Plus 为我们提供了强大的 mapper service 模板,能够大大的提高开发效率
但是在真正开发过程中, MyBatis-Plus 并不能为我们解决所有问题,例如一些复杂的 SQL ,多表
联查,我们就需要自己去编写代码和 SQL 语句,我们该如何快速的解决这个问题呢,这个时候可
以使用 MyBatisX 插件
MyBatisX 一款基于 IDEA 的快速开发插件,为效率而生。
MyBatisX 插件用法: https://baomidou.com/pages/ba5b24/

1.生成代码

1.安装

 下载安装使用,重启

application.yml
spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=UTC
    username: root
    password: woaini520

配置Mysql

 

 

2. 快速生成CRUD

1.先写出关键字

2.选择

 

  • 28
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: Mybatis Plus可以通过多表条件查询来实现复杂的数据查询操作。在进行多表条件查询时,需要使用Mybatis Plus提供的Wrapper对象来构建查询条件,同时需要使用Mybatis Plus提供的Join方法来实现多表关联查询。具体操作步骤如下: 1. 创建Wrapper对象,通过Wrapper对象来构建查询条件。 2. 使用Wrapper对象的Join方法来实现多表关联查询,Join方法需要传入关联的表名和关联条件。 3. 使用Mybatis Plus提供的selectList方法来执行查询操作,该方法需要传入Wrapper对象和查询结果的实体类。 4. 最后,通过查询结果的实体类来获取查询结果。 需要注意的是,在进行多表条件查询时,需要确保关联的表之间存在关联关系,否则查询结果可能会出现错误。同时,还需要注意查询条件的合理性,避免出现查询结果不准确的情况。 ### 回答2: Mybatis Plus是基于Mybatis的一套增强工具,它为我们提供了更为便捷的操作。在实际的项目中,多表条件查询是非常常见的需求,因为有时候需要查询多个表的数据并使用多个条件来筛选数据。 针对此类需求,Mybatis Plus提供了多种多表条件查询的方法,我们可以根据实际情况选择适合自己的方式来完成查询操作。 1. 基础的多表查询 在Mybatis Plus中,我们可以直接使用Mapper的selectList()方法来查询多表数据,需要注意的是,需要使用Mybatis Plus中的Wrapper进行条件拼接。 示例代码: ```java List<UserVo> userList = userMapper.selectList(new QueryWrapper<UserVo>() .eq("u.sex", 1) .like("c.clazz_name", "软件")); ``` 2. 多表查询结果映射对象 如果多表联查的结果是需要映射到自定义的对象中,可以使用Mybatis Plus的selectVo方法。 示例代码: ```java List<UserVo> userList = userMapper.selectVo(new QueryWrapper<UserVo>() .eq("u.sex", 1) .like("c.clazz_name", "软件")); ``` 3. 多表嵌套查询 在Mybatis Plus中,也支持多表嵌套查询,即在一个查询中嵌套多个子查询。 示例代码: ```java List<UserVo> userList = userMapper.selectList(new QueryWrapper<UserVo>() .inSql("u.id", "SELECT user_id FROM user_role WHERE role_id = 1") .inSql("u.class_id", "SELECT id FROM clazz WHERE clazz_name LIKE '%软件%'")); ``` 4. 多表联查分页查询 对于需要分页查询的多表联查,我们可以通过Mybatis Plus中的Page类来实现分页操作。 示例代码: ```java Page<UserVo> page = new Page<>(1, 10); List<UserVo> userList = userMapper.selectPage(page, new QueryWrapper<UserVo>() .eq("u.sex", 1) .like("c.clazz_name", "软件")).getRecords(); ``` 总之,Mybatis Plus为我们提供了多种多表条件查询的方式,我们可以根据实际项目需求来选择适合自己的方法。同时,需要注意的是,在进行多表联查时,不仅需要拼接好查询条件,还要注意多表中字段的别名,确保查询的准确性。 ### 回答3: Mybatis Plus 是 Mybatis 框架的增强工具包,提供了更方便的操作数据库的方式。在多表操作中,Mybatis Plus 提供了一些方便的 API 来实现多表条件查询。 多表条件查询可以通过以下方式实现: 1. 嵌套查询: 可以通过在一个 queryWrapper 中套用其他表的queryWrapper 来实现多表查询,例如: ```java QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("user_type", 1) .inSql("company_id", "select id from company where name like '%金%'"); List<User> userList = userMapper.selectList(queryWrapper); ``` 这段代码中,`queryWrapper.inSql()` 方法用来连接一个 SQL 查询语句,这个 SQL 查询语句会作为一个条件来筛选出符合要求的记录。 2. Left Join: 在多表查询中经常使用左连接来获取所有符合条件的记录,Mybatis Plus 提供了 `leftJoin()` 方法来实现左连接操作。例如: ```java LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(User::getUserType, 1) .leftJoin(Company.class, Company::getId, User::getCompanyId, (comWrapper, userWrapper) -> comWrapper.like(Company::getName, "金%")); List<User> userList = userMapper.selectList(queryWrapper); ``` 这段代码中,我们使用了 LambdaQueryWrapper 来构建查询条件,然后使用 `leftJoin()` 方法进行左连接操作,`leftJoin()` 方法中的第一个参数是连接的表对象,第二个参数是连接表的主键,第三个参数是当前表的外键,第四个参数是查询条件。 3. Where 子句: Mybatis Plus 提供了很多方便的查询条件封装方法,我们可以通过在 queryWrapper 中使用这些方法来实现多表查询。例如: ```java QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("user_type", 1) .inSql("company_id", "select id from company where name like '%金%' ") .apply("id in (select user_id from group_user where group_id = 1)"); List<User> userList = userMapper.selectList(queryWrapper); ``` 这段代码中,我们使用了 `inSql()` 方法和 `apply()` 方法来构建查询条件,这两个方法都可以用来连接一个自定义的 SQL 查询语句作为查询条件。 4. 链式查询: Mybatis Plus 也支持链式查询,可以通过连接查询条件来实现多表查询,例如: ```java userMapper.selectList(new QueryWrapper<User>() .eq("user_type", 1) .inSql("company_id", "select id from company where name like '%金%' ") .apply("id in (select user_id from group_user where group_id = 1)") .nested(qw -> qw .eq("status", 1) .ne("gender", 1) .or() .like("username", "张") ) ); ``` 这段代码中,我们使用了链式查询的方式来构建查询条件。`nested()` 方法可以用来嵌套查询,这里我们使用了嵌套查询来构建更复杂的查询条件。 总之,Mybatis Plus 提供了很多方便的操作数据库的方法,通过这些方法可以很方便的实现多表条件查询。在实际开发中,可以根据具体需求来选择相应的 API 来进行多表查询操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

允歆辰丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值