学习MyBatis-Plus这一篇就够了




一、MyBatis-Plus简介

1.1、简介

MyBatis-Plus(简称 MP)是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为
简化开发、提高效率而生。

我们的愿景是成为 MyBatis 最好的搭档,就像魂斗罗中的 1P、2P,基友搭配,效率翻倍。

在这里插入图片描述
MyBatis-Plus官网地址
在这里插入图片描述

1.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 操作智能分析阻断,也可自定义拦截规则,预防
    误操作

1.3、支持数据库

任何能使用MyBatis进行 CRUD, 并且支持标准 SQL 的数据库,具体支持情况如下

  • MySQL,Oracle,DB2,H2,HSQL,SQLite,PostgreSQL,SQLServer,Phoenix,Gauss ,
    ClickHouse,Sybase,OceanBase,Firebird,Cubrid,Goldilocks,csiidb
  • 达梦数据库,虚谷数据库,人大金仓数据库,南大通用(华库)数据库,南大通用数据库,神通数据
    库,瀚高数据库

1.4、框架结构

在这里插入图片描述



二、入门案例

2.1、开发环境

  • JDK8+
  • maven:3.5.x 以上
  • MySQL:5.7
  • Spring Boot:2.6.3
  • Mybatis-Plus:3.5.1

2.2、创建数据库和表

1.创建数据库和表

CREATE DATABASE `mybatis_plus` /*!40100 DEFAULT CHARACTER SET utf8mb4 */; 
use `mybatis_plus`; 

DROP TABLE IF EXISTS user;

CREATE TABLE user
(
    id BIGINT(20) NOT NULL COMMENT '主键ID',
    name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
    age INT(11) NULL DEFAULT NULL COMMENT '年龄',
    email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
    PRIMARY KEY (id)
);

2.插入数据

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');

2.3、创建工程

1、初始化工程
在这里插入图片描述
在这里插入图片描述
2、添加依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.1</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

3、配置
application.yml 配置文件中添加 mysql 数据库的相关配置:

# MySQL数据库连接
spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false
    username: root
    password: root

mybatis-plus:
  # 配置类型别名
  type-aliases-package: com.laptoy.mybatisplus.pojo
  configuration:
  	#  配置MyBatis日志
	log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹

@SpringBootApplication
@MapperScan("com.laptoy.mybatisplus.mapper")
public class Application {

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

}

4、编码
编写实体类 User.java(此处使用了 Lombok 简化代码)

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

编写 Mapper 包下的 UserMapper接口

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

2.4、开始使用

添加测试类,进行功能测试:

@SpringBootTest
public class SampleTest {
	
	// 类是动态创建的@MapperScan,程序可以正常执行
	// 为了避免报错,可以在mapper接口上添加 @Repository 注解
    @Autowired
    private UserMapper userMapper;

    @Test
    public void testSelect() {
        System.out.println(("----- selectAll method test ------"));
        List<User> userList = userMapper.selectList(null);
        userList.forEach(System.out::println);
    }

}

在这里插入图片描述

2.5、小结

通过以上几个简单的步骤,我们就实现了 User 表的 CRUD 功能,甚至连 XML 文件都不用编写!

从以上步骤中,我们可以看到集成MyBatis-Plus非常的简单,只需要引入 starter 工程,并配置 mapper 扫描路径即可。

但 MyBatis-Plus 的强大远不止这些功能,想要详细了解 MyBatis-Plus 的强大功能?那就继续往下看吧!



三、基本的CRUD

3.1、BaseMapper接口

  • 通用 CRUD 封装BaseMapper 接口,为 Mybatis-Plus 启动时自动解析实体表关系映射转换为 Mybatis 内 部对象注入容器
  • 泛型 T 为任意实体对象
  • 参数 Serializable 为任意类型主键 Mybatis-Plus 不推荐使用复合主键约定每一张表都有自己的唯一 id 主键
  • 对象 Wrapper 为 条件构造器

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

3.1.1、测试插入

@Test
public void testInsert() {
    User user = new User(null, "jiali", 21, "baomi.com");
    //INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
    int result = userMapper.insert(user);
    System.out.println("受影响行数:" + result);
    System.out.println("id自动获取:" + user.getId());
}

在这里插入图片描述

最终执行的结果,所获取的id为1501824207777501186
这是因为MyBatis-Plus在实现插入数据时,会默认基于雪花算法的策略生成id

3.1.2、测试删除

3.1.2.1、根据ID删除
@Test
public void testDeleteById() {
    //DELETE FROM user WHERE id=?
    int result = userMapper.deleteById(1501824207777501186L);
    System.out.println("受影响行数:" + result);
}
3.1.2.2、根据ID批量删除
@Test
public void testDeleteBatchIds(){
    //DELETE FROM user WHERE id IN ( ? , ? , ? )
    List<Long> idList = Arrays.asList(1L, 2L, 3L);
    int result = userMapper.deleteBatchIds(idList);
    System.out.println("受影响行数:"+result);
}
3.1.2.3、根据map条件删除
@Test
public void testDeleteByMap(){
    //DELETE FROM user WHERE name = ? AND age = ?
    Map<String, Object> map = new HashMap<>();
    map.put("age", 21);
    map.put("name", "jiali");
    int result = userMapper.deleteByMap(map);
    System.out.println("受影响行数:"+result);
}

3.1.3、测试修改

传入user对象根据user对象的id进行修改

@Test
public void testUpdateById(){
    User user = new User(4L, "admin", 22, null);
    //UPDATE user SET name=?, age=? WHERE id=?
    int result = userMapper.updateById(user);
    System.out.println("受影响行数:"+result);
}

3.1.4、测试查询

3.1.4.1、根据id查询
@Test
public void testSelectById(){
    //SELECT id,name,age,email FROM user WHERE id=?
    User user = userMapper.selectById(4L);
    System.out.println(user);
}
3.1.4.2、根据多个id查询
@Test
public void testSelectBatchIds(){
    //SELECT id,name,age,email FROM user WHERE id IN ( ? , ? )
    List<Long> idList = Arrays.asList(4L, 5L);
    List<User> list = userMapper.selectBatchIds(idList);
    list.forEach(System.out::println);
}
3.1.4.3、根据map条件查询
@Test
public void testSelectByMap(){
    //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 = userMapper.selectByMap(map);
    list.forEach(System.out::println);
}
3.1.4.4、查询全部
@Test
public void testSelectList(){
	//SELECT id,name,age,email FROM user
	List<User> list = userMapper.selectList(null);
	list.forEach(System.out::println);
}

3.1.5、自定义功能

根据业务需求mabatisplus提供的方法不够用,这个时候就需要自定义映射

Springboot默认配置了mapper映射文件位置,只要放在类路径/mapper/**下就可以自动扫描到,也可以自定义路径
在这里插入图片描述
在这里插入图片描述
@MapKey:以某个字段的值作为key ,将每条数据转换的map集合作为value,放在一个map集合中

/**
 * 根据id查询用户信息为map集合
 */
@MapKey("id")
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.laptoy.mybatisplus.mapper.UserMapper">

    <select id="selectMapById" resultType="java.util.Map">
        select *
        from user
        where id = #{id};
    </select>
</mapper>
@Test
public void testSelectMap() {
    Map<String, Object> map = userMapper.selectMapById(1L);
    System.out.println(map);
}

结果
在这里插入图片描述

3.2、IService接口

  • 通用 Service CRUD 封装IService接口,进一步封装 CRUD 采用 get 查询单行 remove 删除 list 查询集合 page 分页前缀命名方式区分 Mapper 层避免混淆,
  • 泛型 T 为任意实体对象
  • 建议如果存在自定义通用 Service 方法的可能,请创建自己的 xxService 继承 Mybatis-Plus 提供的基类,再将自己的 xxServiceImpl 实现 xxService 且继承基类的实现类
  • 对象 Wrapper 为 条件构造器

IService接口源码地址

保证高可扩展性

1、Userservice接口先继承IService接口

public interface UserService extends IService<User> {
}

在这里插入图片描述

2、UserSerivceImpl实现当前UserService并继承IService接口的实现类ServiceImpl,这样就不需要手动重写所有的IService方法

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

在这里插入图片描述

3.2.1、测试查询记录数

@Test
public void testGetCount() {
    long count = userService.count();
    System.out.println("总记录数:" + count);
}

3.2.2、测试批量添加

SQL长度有限制,海量数据插入单条SQL无法实行
因此MP将批量插入放在了通用Service中实现,而不是通用Mapper

@Test
public void testSaveBatch() {
    // SQL长度有限制,海量数据插入单条SQL无法实行
    // 因此MP将批量插入放在了通用Service中实现,而不是通用Mapper
    ArrayList<User> users = new ArrayList<>();
    for (int i = 0; i < 5; i++) {
        User user = new User();
        user.setName("user:" + i);
        user.setAge(20 + i);
        users.add(user);
    }
    //SQL:INSERT INTO t_user ( username, age ) VALUES ( ?, ? )
    userService.saveBatch(users);
}


四、常用注解

4.1、@TableName

  • 描述:表名注解,标识实体类对应的表
  • 使用位置:实体类
  • 解决数据库表名与实体类不一致的问题

单个实体类配置

@TableName("t _user")
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

全局配置application.yml配置全局表名前缀

mybatis-plus:
  global-config:
    db-config:
      table-prefix: t_

4.2、@TableId

4.2.1、简介及使用

  • 描述:主键注解
  • 使用位置:实体类主键字段
  • MybatisPlus默认会将名为id字段的作为主键字段,如果数据表主键字段不叫id,需要使用@TableId指定
  • 所以建议实体类主键字段都加@TableId

如果实体类与数据表主键的字段名不一致,可以使用@TableId来指定

测试:将数据表id字段改为uid
在这里插入图片描述
如果不使用@TableId指定uid属性会报错,因为MB默认配置会自动搜索主键字段名为id,MB会不知道用谁当主键

public class User {
    @TableId
    private Long uid;
    private String name;
    private Integer age;
    private String email;
}

4.2.2、@TableId的value属性

如果实体类与数据表主键的字段名不一致,可以使用value属性来指定

例如数据表字段为uid实体类属性为 id

public class User {
    @TableId("uid")
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

4.2.3、@TableId的type属性

  • 设置主键生成策略
  • 默认使用雪花算法计算出20为id值
  • 可以使用IdType属性来自定义id规则
  • 如果插入时指定了id值那么这些type配置失效,id为设置的值

测试:数据表主键字段设置自动递增
在这里插入图片描述

public class User {
    @TableId(value = "uid", type = IdType.AUTO)
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

在这里插入图片描述

4.2.4、设置全局主键生成策略

mybatis-plus:
  global-config:
    db-config:
      id-type: xxx

4.3、雪花算法

雪花算法是由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碰撞,并且效率较高。

4.4、@TableField

解决实体类属性与数据库字段不一致问题

例如数据表字段为uname实体类属性为 name
测试:修改数据表name属性为uname,此时如果实体类不指定uname属性就会报错

public class User {
    @TableId(value = "uid", type = IdType.AUTO)
    private Long id;
    
    @TableField("uname")
    private String name;
    private Integer age;
    private String email;
}

4.5、@TableLogic

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

使用该注解后在进行任何增删改查时都会加上关于逻辑字段条件的语句
例如配置该注解在isDeleted字段,进行增删改查时就会自动带上关于is_deleted的字段

测试:

数据表添加字段id_deleted默认值为0,类型为int

实体类添加属性,MB默认开启驼峰命名法

@TableLogic
private Integer isDeleted;

执行删除:删除的时候自动加上删除条件 is_deleted=0

@Test
public void delete() {
	// UPDATE user SET is_deleted=1 WHERE uid=? AND is_deleted=0
    userMapper.deleteById(1L);
}

执行查询:查询的时候自动加上查询条件 is_deleted = 0

@Test
public void testSelectList() {
    // SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0
    List<User> list = userMapper.selectList(null);
    list.forEach(System.out::println);
}

在这里插入图片描述

id为1的数据查询不到,但数据库里仍有,因为逻辑只是把is_deleted字段改为1了



五、条件构造器

5.1、Wrapper介绍

在这里插入图片描述

  • Wrapper : 条件构造抽象类,最顶端父类
    • AbstractWrapper : 查询条件封装,生成 sql 的 where 条件
      • QueryWrapper : 查询条件封装
      • UpdateWrapper : Update 条件封装,能够直接set值而不需要new一个对象再调用set属性()方法
      • AbstractLambdaWrapper : 使用Lambda 语法保证column不会写错类::属性名
        • LambdaQueryWrapper :Lambda语法使用的查询Wrapper
        • LambdaUpdateWrapper : Lambda 更新封装Wrapp
参数说明
eq等于
gt大于
lt小于
between两者之间
like模糊查询
isNull为空
in在…之间
or或拼接
andand嵌套

更多请查看MyBatisPlus官网关于条件构造器

5.2、QueryWrapper

5.2.1、组装查询条件

@Test
public void test01() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    //SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0 AND (name LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
    wrapper.like("name", "a").between("age", 20, 30).isNotNull("email");
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

5.2.2、组装排序条件

查询用户信息,按照年龄的降序排序,若年龄相同按照id升序排序

@Test
public void test02() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    // SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0 ORDER BY age DESC,id ASC
    wrapper.orderByDesc("age").orderByAsc("id");
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

5.2.3、组装删除条件

前面添加了逻辑删除,所以这里还是修改is_deleted字段

@Test
public void test03() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    // UPDATE user SET is_deleted=1 WHERE is_deleted=0 AND (email IS NULL)
    wrapper.isNull("email");
    int delete = userMapper.delete(wrapper);
}

5.2.4、条件优先级

5.2.4.1、组装修改条件一

年龄大于20 用户名包含a邮箱为null的用户信息修改

@Test
public void test04() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.gt("age", 20)
    	   .like("name", "a")
    	   .or()
    	   .isNull("email");
    
    User user = new User();
    user.setName("jiali");
    user.setEmail("aaa.com");
    // UPDATE user SET name=?, email=? WHERE is_deleted=0 AND (age > ? AND name LIKE ? OR email IS NULL)
    userMapper.update(user, wrapper);
}
5.2.4.2、组装修改条件二(Lamda表达式)

用户名包含a 并且 (年龄大于20或邮箱为null)的用户信息修改

@Test
public void test05() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.like("name", "a")
   		   .and(i -> i.gt("age", 20).or().isNull("email"));
    
    User user = new User();
    user.setName("jiali");
    user.setEmail("aaa.com");
    // UPDATE user SET name=?, email=? WHERE is_deleted=0 AND (name LIKE ? AND (age > ? OR email IS NULL))
    userMapper.update(user, wrapper);
}

5.2.5、组装select语句

查询表的部分字段

@Test
public void test06() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    // //SELECT id,name FROM user
    wrapper.select("id", "name");
    // selectMaps()返回Map集合列表,通常配合select()使用,避免User对象中没有被查询到的列值 为null
    List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
    maps.forEach(System.out::println);
}

5.2.6、组装子查询

查询id小于等于3的用户信息

@Test
public void test07() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.inSql("id", "select id from user where id <=3");
    // SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE (id IN (select id from t_user where id <= 3))
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

5.3、UpdateWrapper

可以设置条件及修改的字段,不需要new实体类进行set赋值

@Test
public void test08() {
    UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
    updateWrapper.like("name", "a").and(i ->
            i.gt("age", 20).isNull("email")
    );
    // 设置修改的字段
    updateWrapper.set("name","jiali").set("email","jiali.com");
    userMapper.update(null,updateWrapper);
}

5.4、模拟开发组装条件

@Test
public void test09() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    Integer ageBegin = 10;
    Integer ageEnd = 30;
    String name = "a";
    if (StringUtils.isNotBlank(name)) {
        wrapper.like("name", name);
    }
    if (ageBegin != null) {
        wrapper.gt("age", ageBegin);
    }
    if (ageEnd != null) {
        wrapper.lt("age", ageEnd);
    }
    // SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0 AND (name LIKE ? AND age > ? AND age < ?)
    userMapper.selectList(wrapper);
}

5.5、使用condition组装条件

很多方法都内置了condition属性在这里插入图片描述

直接使用condition进行条件装配优化上述5.4的情况

@Test
public void test10() {
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    Integer ageBegin = 10;
    Integer ageEnd = 30;
    String name = "a";
    wrapper.like(StringUtils.isNotBlank(name), "name", name)
            .ge(ageBegin != null, "age", ageBegin)
            .le(ageEnd != null, "age", ageEnd);
    userMapper.selectList(wrapper);
}

5.6、LambdaQueryWrapper

能够防止字段名写错,使用类::get属性代替人为输入字段名

@Test
public void test11() {
    String name = "a";
    LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    lambdaQueryWrapper.like(StringUtils.isNotBlank(name), User::getName, name);
    // SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0 AND (name LIKE ?)
    List<User> users = userMapper.selectList(lambdaQueryWrapper);
    users.forEach(System.out::println);
}

5.7、LambdaUpdateWrapper

能够防止字段名写错,使用类::get属性代替人为输入字段名

@Test
public void test12() {
    LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
    wrapper.like(User::getName, "a");
    // 设置修改的字段
    wrapper.set(User::getName, "jiali");
    userMapper.update(null, wrapper);
}

·



六、插件

6.1、分页插件

1、配置类指定拦截数据源为MYSQL

@Configuration
@MapperScan("com.laptoy.mybatisplus.mapper")
public class MBConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}
@Test
public void test01() {
    Page<User> page = new Page<>(1, 3);
    // SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0 LIMIT ?
    mapper.selectPage(page, null);
}

2、selectPage方法解析
在这里插入图片描述
Page继承IPage,所以可以直接传入Page对象
在这里插入图片描述
3、分页相关数据获取

@Test
public void test01() {
    Page<User> page = new Page<>(2, 3);
    // SELECT id,name,age,email,is_deleted FROM user WHERE is_deleted=0 LIMIT ?
    mapper.selectPage(page, null);
    System.out.println(page.getRecords());
    System.out.println(page.getPages());
    System.out.println(page.getTotal());
    System.out.println(page.hasNext());
}

在这里插入图片描述

6.2、自定义分页功能

Page MBP提供的分页对象,必须位于参数第一个位置
返回值也必须为Page

/**
 * 通过年龄查询用户信息并分页
 * @param page MBP提供的分页对象,必须位于参数第一个位置
 */
Page<User> selectPageVo(@Param("page") Page<User> page, @Param("age") Integer age);

千万别加;这个符号在后面,因为后面MBP还要进行拼接sql语句limit参数

<select id="selectPageVo" resultType="User">
    select id, name, age from USER where age > #{age}
</select>
@Test
public void test02() {
    Page<User> page = new Page<>(1, 3);
    mapper.selectPageVo(page, 20);
    System.out.println(page.getRecords());
    System.out.println(page.getPages());
    System.out.println(page.getTotal());
    System.out.println(page.hasNext());
}

6.3、乐观锁

OptimisticLockerInnerInterceptor

当要更新一条记录的时候,希望这条记录没有被别人更新

乐观锁实现方式:

  • 取出记录时,获取当前 version
  • 更新时,带上这个 version
  • 执行更新时, set version = oldVersion+1 where version = oldVersion
  • 如果 version 不对,就更新失败

取出记录时,获取当前version
SELECT id,name,price,version FROM product WHERE id=1
更新时,version + 1,如果where语句中的version版本不对,则更新失败
UPDATE product SET price=price+50, version=version + 1 WHERE id=1 AND version=1

模拟乐观锁
1、数据表及数据

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, 'macbook', 100);

2、实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@TableName("t_product")
public class Product {
    @TableId
    private Long id;
    private String name;
    private Integer price;
    @Version
    private Integer version;
}

3、配置类

@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
    return interceptor;
}

在这里插入图片描述
代码在这

@Test
public void test03() {
    Product p1 = productMapper.selectById(1L);   // 价格:100 版本:0
    Product p2 = productMapper.selectById(1L);   // 价格:100 版本:0
    p1.setPrice(p1.getPrice() + 50);
    int result1 = productMapper.updateById(p1);  // 成功 价格:150 版本:1
    System.out.println("p1修改后的结果" + result1);// 1
    p2.setPrice(p2.getPrice() - 30);
    int result2 = productMapper.updateById(p2);  // 失败,因为p2的版本为0
    System.out.println("p2修改后的结果" + result2);// 失败
    if (result2 == 0) {
        p2 = productMapper.selectById(1L);       // 价格:150 版本:1
        p2.setPrice(p2.getPrice() - 30);
        result2 = productMapper.updateById(p2);  // 价格:120 版本:2
        System.out.println("p2再次修改后的结果" + result2);
    }
    Product customer = productMapper.selectById(1L);
    System.out.println("用户看到的价格" + customer.getPrice());  // 120
}

说明

  • 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
  • 整数类型下 newVersion = oldVersion + 1
  • newVersion 会回写到 entity 中
  • 仅支持 updateById(id) 与 update(entity, wrapper) 方法
  • 在 update(entity, wrapper) 方法下, wrapper 不能复用!!!

·



七、通用枚举

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

1、数据库添加int类型 sex字段
2、创建枚举类

@EnumValue 标记数据库存的值是code

@Getter
public enum SexEnum {

    MALE(1, "男"),
    FAMALE(2, "女");
		
	// 将该字段作为值存入数据库,否则会默认将value(MALE,FAMALE)放入数据库
	@EnumValue
    private Integer sex;
    private String sexName;

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

3、User类添加字段private SexEnum sex;

4、配置文件扫描枚举类所在包

mybatis-plus:
  type-enums-package: com.laptoy.mybatisplus.enums

5、测试

@Test
public void test04() {
    User user = new User();
    user.setName("laptoy");
    user.setSex(SexEnum.MALE);
    mapper.insert(user);
}

在这里插入图片描述

·



八、代码生成器

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.5.2</version>
</dependency>
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.31</version>
</dependency>

修改自定义配置后直接执行

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

·



九、多数据源

1、引入dynamic-datasource-spring-boot-starter

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

2、配置数据源

spring:
  datasource:
    dynamic:
      primary: master #设置默认的数据源或者数据源组,默认值即为master
      strict: false   #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/laptoy_user
          username: root
          password: root
          driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置
        slave_1:
          url: jdbc:mysql://localhost:3306/laptoy_product
          username: root
          password: root
          driver-class-name: com.mysql.jdbc.Driver
# 多主多从                      纯粹多库(记得设置primary)                   混合配置
spring:                               spring:                               spring:
  datasource:                           datasource:                           datasource:
    dynamic:                              dynamic:                              dynamic:
      datasource:                           datasource:                           datasource:
        master_1:                             mysql:                                master:
        master_2:                             oracle:                               slave_1:
        slave_1:                              sqlserver:                            slave_2:
        slave_2:                              postgresql:                           oracle_1:
        slave_3:                              h2:                                   oracle_2:

3、使用 @DS 切换数据源
@DS 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 上注解。

注解结果
没有@DS默认数据源
@DS(“dsName”)dsName可以为组名也可以为具体某个库的名称
@Service
@DS("master")
public class UserServiceImpl implements UserService {

  @Autowired
  private UserMapper userMapper;
  @Autowired
  private ProductMapper productMapper;

  public List selectByMaster() {
    return  userMapper.selectList();
  }
  
  @Override
  @DS("slave_1")
  public List selectBySlave() {
    return  productMapper.selectList();
  }
}

·



十、MyBatisX插件

MybatisX 是一款基于 IDEA 的快速开发插件,为效率而生 官网地址
在这里插入图片描述

10.1、定位接口与映射文件

在这里插入图片描述
在这里插入图片描述

10.2、代码快速生成

在这里插入图片描述
测试连接
在这里插入图片描述
设置显示的数据库
在这里插入图片描述
1、
在这里插入图片描述
2、
在这里插入图片描述
3、
在这里插入图片描述
结果展示
在这里插入图片描述

10.3、快速生成CRUD

在这里插入图片描述
Alt+Insert
在这里插入图片描述
在这里插入图片描述

·



  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Laptoy

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

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

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

打赏作者

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

抵扣说明:

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

余额充值