MyBatisPlus----基础

目录

  • 1 MyBatisPlus简介
    • 1.1 入门案例
    • 1.2 MyBatisPlus概述
  • 2 标准数据层开发
    • 2.1 标准数据层CRUD功能
    • 2.2 Lombok简化实体类开发
    • 2.3 分页查询功能
  • 3 DQL编程控制
    • 3.1 条件查询的三种格式
    • 3.2 条件查询null值处理
    • 3.3 查询投影
      • 3.3.1 查询指定字段
      • 3.3.2 聚合查询
      • 3.3.3 分组查询
    • 3.4 查询条件
    • 3.5 映射匹配兼容性
  • 4 DML编程控制
    • 4.1 id生成策略
    • 4.2 全局配置
    • 4.3 多记录操作
    • 4.4 逻辑删除
    • 4.5 乐观锁
  • 5 快速开发
    • 5.1 代码生成器(了解)

1 MyBatisPlus简介

1.1 入门案例

  • MybatisPlus(简称MP)是基于MyBatis框架基础上开发的增强型工具,旨在简化开发、提供效率。
  • 开发方式:
    • 基于MyBatis使用MyBatisPlus
    • 基于Spring使用MyBatisPlus
    • 基于SpringBoot使用MyBatisPlus

关键步骤:导入mybatis-plus-boot-starter依赖,dao继承BaseMapper<实体类名>

  1. 创建数据库及表
create database if not exists mybatisplus_db character set utf8;
use mybatisplus_db;
CREATE TABLE user (
    id bigint(20) primary key auto_increment,
    name varchar(32) not null,
    password  varchar(32) not null,
    age int(3) not null ,
    tel varchar(32) not null
);
insert into user values(1,'Tom','tom',3,'18866668888');
insert into user values(2,'Jerry','jerry',4,'16688886666');
insert into user values(3,'Jock','123456',41,'18812345678');
insert into user values(4,'传智播客','itcast',15,'4006184000');
  1. 创建SpringBoot工程
    MyBatisPlus----基础_第1张图片
    勾选配置使用技术,只使用mysql,不用添加Mybatis,因为后面导入MybatisPlus依赖,会根据依赖传递自动导入Mybatis、Mybatis整合spring的依赖。
    MyBatisPlus----基础_第2张图片

说明:由于MP并未被收录到idea的系统内置配置,无法直接选择加入,需要手动在pom.xml中配置添加

  1. :pom.xml添加MybatisPlus和druid依赖
<dependency>
    <groupId>com.baomidougroupId>
    <artifactId>mybatis-plus-boot-starterartifactId>
    <version>3.4.1version>
dependency>
<dependency>
    <groupId>com.alibabagroupId>
    <artifactId>druidartifactId>
    <version>1.1.16version>
dependency>

druid数据源可以加也可以不加,SpringBoot有内置的数据源,可以配置成使用Druid数据源
从MP的依赖关系可以看出,通过依赖传递已经将MyBatis与MyBatis整合Spring的jar包导入,我们不需要额外在添加MyBatis的相关jar包
MyBatisPlus----基础_第3张图片

  1. 添加MP的相关配置信息
spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    #serverTimezone是用来设置时区,UTC是标准时区,和咱们的时间差8小时,所以可以将其修改为Asia/Shanghai
    url: jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC
    username: root
    password: root
  1. 创建实体类User
public class User {   
    private Long id;    //注意id类型是Long
    private String name;
    private String password;
    private Integer age;
    private String tel;
    //setter...getter...toString
}
  1. 创建Dao接口,继承BaseMapper
@Mapper
public interface UserDao extends BaseMapper<User>{
}
  1. 测试类中注入dao接口,测试功能
@SpringBootTest
class MybatisplusQuickstartApplicationTests {
    @Autowired
    private UserDao userDao;

    @Test
    void testGetAll() {
        List<User> userList = userDao.selectList(null);
        System.out.println(userList);
    }
}

1.2 MyBatisPlus概述

  • MybatisPlus(简称MP)是基于MyBatis框架基础上开发的增强型工具,旨在简化开发、提供效率。
  • 官网:https://mp.baomidou.com/   https://mybatis.plus/
  • MyBatisPlus特效
    • 无侵入:只做增强不做改变,不会对现有工程产生影响
    • 强大的 CRUD 操作:内置通用 Mapper,少量配置即可实现单表CRUD 操作
    • 支持 Lambda:编写查询条件无需担心字段写错
    • 支持主键五种自动生成策略
    • 内置分页插件、代码生成器、全局拦截插件、sql注入剥离器(支持sql注入剥离,防止SQL注入攻击)
    • ……

2 标准数据层开发

2.1 标准数据层CRUD功能

MyBatisPlus----基础_第4张图片测试代码:

@SpringBootTest
class MybatisplusQuickstartApplicationTests {
    @Autowired
    private UserDao userDao;

    @Test
    void testSave(){
        User user = new User();
        user.setName("zhangsan");
        user.setPassword("123");
        user.setAge(18);
        user.setTel("123321");
        userDao.insert(user);
    }

    @Test
    void testDelete(){
        userDao.deleteById(2L);
    }

    @Test
    void testUpdate(){
        User user = new User();
        user.setId(1L);
        user.setName("Tom666");
        user.setPassword("tom666");
        userDao.updateById(user);
    }

    @Test
    void testSelectById(){
        System.out.println(userDao.selectById(3L));
    }

    @Test
    void testSelectAll(){
        List<User> userList = userDao.selectList(null);
        System.out.println(userList);
    }
}

新增insert:
MyBatisPlus----基础_第5张图片
删除deleteById:
MyBatisPlus----基础_第6张图片
修改uodateById:
MyBatisPlus----基础_第7张图片
根据id查询selectById:
在这里插入图片描述
查询全部selectList:
在这里插入图片描述
问题:新增的时候发现id是一个很长的数据
解决方案:修改id主键自增策略(两种方式)

方式一:这里主键ID是雪花算法ASSIGN_ID策略生成的,想改成自增在实体类id上注解

@TableId(type = IdType.AUTO)

方式二:配置类yml全局配置id策略ASSIGN_ID:

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

AUTO策略:数据库默认自增策略
NONE: 不设置id生成策略
INPUT:用户手工输入id,如果id为null会报错
ASSIGN_ID:雪花算法生成id(可兼容数值型与字符串型),mp默认id策略
ASSIGN_UUID:以UUID生成算法作为id生成策略
其他的几个策略均已过时,都将被ASSIGN_ID和ASSIGN_UUID代替

2.2 Lombok简化实体类开发

  • Lombok,一个Java类库,提供了一组注解,简化POJO实体类开发
<dependency>
    <groupId>org.projectlombokgroupId>
    <artifactId>lombokartifactId>
    
dependency>

注意:版本可以不用写,因为SpringBoot中已经管理了lombok的版本。

  • Lombok常见的注解有:
    • @Setter:为模型类的属性提供setter方法
    • @Getter:为模型类的属性提供getter方法
    • @ToString:为模型类的属性提供toString方法
    • @EqualsAndHashCode:为模型类的属性提供equals和hashcode方法
    • @Data:是个组合注解,包含Setter、Getter、ToString、EqualsAndHashCode
    • @NoArgsConstructor:提供一个无参构造函数
    • @AllArgsConstructor:提供一个包含所有参数的构造函数

2.3 分页查询功能

  1. 设置分页插件拦截器作为Spring管理的bean

说明:可以查看官方文档类配置
MyBatisPlus----基础_第8张图片

//注解为配置类@Configuration,也可以在引导类@Import({MybatisPlusConfig.class})
@Configuration
public class MybatisPlusConfig {
    //被Spring容器管理    
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        //1 创建mp拦截器对象MybatisPlusInterceptor
        MybatisPlusInterceptor mpInterceptor=new MybatisPlusInterceptor();
        //2 添加内置拦截器,参数为分页内置拦截器对象PaginationInnerInterceptor
        mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mpInterceptor;
    }
}
  1. 测试类
    分页查询使用的方法是:
IPage<T> selectPage(IPage<T> page, Wrapper<T> queryWrapper)
//IPage:用来构建分页查询条件
//Wrapper:译为包装器,封装器。用来构建条件查询的条件,目前我们没有可直接传为Null

IPage是一个接口,我们需要找到它的实现类来构建它,具体的实现类,可以进入到IPage类中按ctrl+h,会找到其有一个实现类为Page。

@Test
void testSelectPage(){
    //1 创建IPage分页对象,设置分页参数,1为当前页码,3为每页显示的记录数
    IPage<User> page=new Page<>(1,3);
    //2 执行分页查询
    userDao.selectPage(page,null);
    //3 获取分页结果,不配置拦截器就只能获取到current和size的数据,pages,total都是0,records是全部数据
    System.out.println("当前页码值:"+page.getCurrent());
    System.out.println("每页显示数:"+page.getSize());
    System.out.println("一共多少页:"+page.getPages());
    System.out.println("一共多少条数据:"+page.getTotal());
    System.out.println("数据:"+page.getRecords());
}

运行结果:
在这里插入图片描述

  1. 查看MybatisPlus标准日志
  • 通过MyBatisPlus日志查看一下执行的分页查询sql语句
  • 修改application.yml配置文件
mybatis-plus:
  configuration:
    #打印SQL日志到控制台
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl 

MyBatisPlus----基础_第9张图片

3 DQL编程控制

3.1 条件查询的三种格式

  • MyBatisPlus将书写复杂的SQL查询条件进行了封装,使用编程的形式完成查询条件的组合。
  • 这个我们在前面都有见过,比如查询所有和分页查询的时候,都有看到过一个Wrapper类,这个类就是用来构建查询条件的
    MyBatisPlus----基础_第10张图片
  • 包装器Wrapper是接口,实际开发中主要使用它的两个实现类:QueryWrapperLambdaQueryWrapper
    MyBatisPlus----基础_第11张图片

两种方式各有优劣:
QueryWrapper存在属性名写错的危险,但是支持聚合、分组查询;
LambdaQueryWrapper没有属性名写错的危险,但不支持聚合、分组查询

  • 基本比较操作

    • eq:等于 =
    • alleq:全部eq(或个别isNull)
      例子:allEq({id:1,name:“老王”,age:null})—>id = 1 and name = ‘老王’ and age is null
    • ne:不等于 <>
    • gt:大于 >
    • ge:大于等于 >=
    • lt:小于 <
    • le:小于等于 <=
    • between:BETWEEN 值1 AND 值2
    • notBetween:NOT BETWEEN 值1 AND 值2
    • in:字段 IN (value.get(0), value.get(1), …)
    • notIn:字段 NOT IN (v0, v1, …)
  • 排除控制台日志打印、banner、log

解决控制台打印日志过多的相关操作可以不用去做,一般会被用来方便我们查看程序运行的结果

  1. 配置取消初始化spring日志打印,取消初始化spring日志打印,resources目录下添加logback.xml,名称固定

<configuration>
configuration>
  1. 取消MybatisPlus启动banner图标,并开启StdOutImpl日志
#mybatis-plus日志控制台输出
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    banner: off  关闭mybatisplus启动图标
  1. 取消SpringBoot的log打印
spring:
  main:
    banner-mode: off  #关闭SpringBoot启动图标(banner)
  • 构建条件查询(三种方式)
  1. 常规格式:QueryWrapper
@Test
void testGetAll(){
    QueryWrapper qw = new QueryWrapper();
    //SELECT id,name,password,age,tel FROM user WHERE (age < ?)
    qw.lt("age",18);
    List<User> userList = userDao.selectList(qw);
    System.out.println(userList);
}

写条件的时候,容易出错,比如age写错,就会导致查询不成功

  1. lambda格式:QueryWrapper基础上加上lambda
@Test
void testGetAll(){
    QueryWrapper<User> qw = new QueryWrapper<User>();
    qw.lambda().lt(User::getAge,18);
    List<User> userList = userDao.selectList(qw);
    System.out.println(userList);
}

注意:构建LambdaQueryWrapper的时候泛型不能省。

  1. lambda格式:LambdaQueryWrapper (推荐使用)
@Test
void testGetAll(){
    LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
    lqw.lt(User::getAge,18);
    List<User> userList = userDao.selectList(lqw);
    System.out.println(userList);
}
  1. 链式编程格式
  • 如果有多个条件可以使用链式编程格式构建
@Test
void testGetAll(){
    LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
    //SELECT id,name,password,age,tel FROM user WHERE (age < ? AND age > ?)
    lqw.lt(User::getAge, 30).gt(User::getAge, 10);
    List<User> userList = userDao.selectList(lqw);
    System.out.println(userList);
}
  • 多条件查询默认是and,or要用.or( )
@Test
void testGetAll(){
    LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
    //SELECT id,name,password,age,tel FROM user WHERE (age < ? OR age > ?)
    lqw.lt(User::getAge, 10).or().gt(User::getAge, 30);
    List<User> userList = userDao.selectList(lqw);
    System.out.println(userList);
}

3.2 条件查询null值处理

问题:区间条件查询,例如某宝筛选价格范围,用户只填最高价,最低价为null不算在查询条件里。
解决思路:查询数据库表中,根据输入年龄范围来查询符合条件的记录,用户在输入值的时候,如果只输入第一个框,说明要查询大于该年龄的用户;如果只输入第二个框,说明要查询小于该年龄的用户;如果两个框都输入了,说明要查询年龄在两个范围之间的用户。
解决方法:新建实体类继承原实体类,多出属性范围,使用lqw.lt(null!=uq.getAge2(),User::getAge, uq.getAge2());进行判定

问题:后台如果想接收前端的两个数据,我们可以使用两个简单数据类型,也可以使用一个模型类。那么使用一个age属性,如何去接收页面上的两个值呢?
方案一(不推荐):添加属性age2,这种做法可以但是会影响到原模型类的属性内容
方案二(推荐):在domain.query下新建一个模型类,让其继承User类,并在其中添加age2属性,UserQuery在拥有User属性后同时添加了age2属性

@Data
public class User {
    private Long id;
    private String name;
    private String password;
    private Integer age;
    private String tel;
}
 
@Data
public class UserQuery extends User {
    private Integer age2;
}

区间条件查询代码实现:
方案一:(不推荐使用)

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
 
    @Autowired
    private UserDao userDao;
    
    @Test
    void testGetAll(){
        //模拟页面传递过来的查询数据
        UserQuery uq = new UserQuery();
        uq.setAge(10);
        uq.setAge2(30);
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
        if(null != uq.getAge2()){
            lqw.lt(User::getAge, uq.getAge2());
        }
        if( null != uq.getAge()) {
            lqw.gt(User::getAge, uq.getAge());
        }
        List<User> userList = userDao.selectList(lqw);
        System.out.println(userList);
    }
}

如果条件多的话,每个条件都需要判断,代码量就比较大

方案二:推荐使用

@SpringBootTest
class Mybatisplus02DqlApplicationTests {
 
    @Autowired
    private UserDao userDao;
    
    @Test
    void testGetAll(){
        //模拟页面传递过来的查询数据
        UserQuery uq = new UserQuery();
        uq.setAge(10);
        uq.setAge2(30);
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
        //第一个参数为判断条件
        lqw.lt(null!=uq.getAge2(),User::getAge, uq.getAge2());
        lqw.gt(null!=uq.getAge(),User::getAge, uq.getAge());
        List<User> userList = userDao.selectList(lqw);
        System.out.println(userList);
    }
}

也可以使用链式编程

lt()方法
在这里插入图片描述
condition为boolean类型,返回true,则添加条件,返回false则不添加条件

3.3 查询投影

目前我们在查询数据的时候,什么都没有做默认就是查询表中所有字段的内容
查询投影:不查询所有字段,只查询出指定字段的数据

3.3.1 查询指定字段

  • 查询结果包含模型类中部分属性
//查询部分属性
//QueryWrapper qw = new QueryWrapper<>();
//qw.select("id","name","age","tel");
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.select(User::getAge,User::getId,User::getName);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);

查询结果:
MyBatisPlus----基础_第12张图片

3.3.2 聚合查询

聚合查询不能用Lambda,只能用QueryWrapper

需求:聚合函数查询,完成count、max、min、avg、sum的使用

  • 查询结果包含模型类中未定义的属性
QueryWrapper<User> qw = new QueryWrapper<User>();
qw.select("count(*) as count");
//qw.select("max(age) as maxAge");
//qw.select("min(age) as minAge");
//qw.select("sum(age) as sumAge");
//qw.select("avg(age) as avgAge");
List<Map<String, Object>> maps = userDao.selectMaps(qw);
System.out.println(maps);

查询结果:
MyBatisPlus----基础_第13张图片

3.3.3 分组查询

分组查询一定要配合聚合函数使用

QueryWrapper<User> qw = new QueryWrapper<User>();
qw.select("count(*) as count,tel");
qw.groupBy("tel");
List<Map<String, Object>> maps = userDao.selectMaps(qw);
System.out.println(maps);

查询结果:
MyBatisPlus----基础_第14张图片

注意:

  • 聚合与分组查询,无法使用lambda表达式来完成
  • MP只是对MyBatis的增强,如果MP实现不了,就在DAO接口中使用MyBatis的方式实现

3.4 查询条件

  • 查询条件:
    • 范围匹配(> 、 = 、between)
    • 模糊匹配(like)
    • 空判定(null)
    • 包含性匹配(in)
    • 分组(group)
    • 排序(order)
    • ……
  1. 等值查询:eq
//=(eq匹配)
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.eq(User::getName,"Jerry").eq(User::getPassword,"jerry");
User loginUser = userDao.selectOne(lqw);
System.out.println(loginUser);

查询结果:
MyBatisPlus----基础_第15张图片
不匹配就返回null

  1. 范围查询:lt le gt ge between
//范围查询:lt le gt ge between
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.le(User::getAge,30).gt(User::getAge,10);
//lqw.between(User::getAge,10,30);
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);

between需要注意两个数值的大小顺序

  1. 模糊查询:like,likeLfte,likeRight
//模糊查询:like likeLeft likeRight
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.like(User::getName,"J");    //%J%
//lqw.likeRight(User::getName,"J");   //J%
//lqw.likeLeft(User::getName,"J");    //%J
List<User> userList = userDao.selectList(lqw);
System.out.println(userList);
  1. 分组查询:groupBy
QueryWrapper<User> qw = new QueryWrapper<User>();
qw.select("count(*) as count,tel");
qw.groupBy("tel");
List<Map<String, Object>> maps = userDao.selectMaps(qw);
System.out.println(maps);
  1. 排序查询:order
//排序 orderBy
LambdaQueryWrapper<User> lwq = new LambdaQueryWrapper<>();
/**
 * condition :条件,返回boolean,
 当condition为true,进行排序,如果为false,则不排序
 * isAsc:是否为升序,true为升序,false为降序
 * columns:需要操作的列
 */
lwq.orderBy(true,false, User::getAge);
userDao.selectList(lwq);

orderByAsc/Desc(columns):按照指定字段进行升序/降序

更多查询条件参考官方API:https://mp.baomidou.com/guide/wrapper.html#abstractwrapper

3.5 映射匹配兼容性

问题一:表字段与编码属性设计不同步
MyBatisPlus----基础_第16张图片
解决方案:@TableField字段注解里的value属性起别名

问题二:编码中添加了数据库中未定义的属性
MyBatisPlus----基础_第17张图片
解决方案:@TableField注解里的exist属性设置其是否在数据库存在

问题三:采用默认查询开放了更多的字段查看权限
MyBatisPlus----基础_第18张图片
解决方案:@TableField注解里的select属性,设置属性是否参与查询

问题四:表名与编码开发设计不同步
MyBatisPlus----基础_第19张图片
解决方案:注解@TableName来设置表与模型类之间的对应关系

四个问题的解决方案代码:
MyBatisPlus----基础_第20张图片

4 DML编程控制

4.1 id生成策略

  • 不同的表应用不同的id生成策略

    • 日志:自增(1,2,3,4,……)
    • 购物订单:特殊规则(FQ23948AK3843)
    • 外卖单:关联地区日期等信息(10 04 20200314 34 91)
    • 关系表:可省略id
    • ……
  • 不同的业务采用的ID生成方式应该是不一样的,那么在MP中都提供了主键生成策略一个注解叫@TableId
    MyBatisPlus----基础_第21张图片

  • 5种id策略

    • AUTO策略:数据库默认自增策略
    • NONE: 不设置id生成策略
    • INPUT:用户手工输入id,如果id为null会报错
    • ASSIGN_ID:雪花算法生成id(可兼容数值型与字符串型),mp默认id策略
    • ASSIGN_UUID:以UUID生成算法作为id生成策略

4.2 全局配置

  • 配置方法设置id策略和实体类前缀tbl_
    MyBatisPlus----基础_第22张图片
    MP会默认将模型类的类名名首字母小写作为表名使用,假如数据库表的名称都以tbl_开头,那么我们就需要将所有的模型类上添加@TableName,简化可以在配置文件中配置
mybatis-plus:
  global-config:
    db-config:
    	table-prefix: tbl_
  • 模型类主键策略设置
    MyBatisPlus----基础_第23张图片
    在配置文件中添加如下内容,就能让所有的模型类都可以使用该主键ID策略:
mybatis-plus:
  global-config:
    db-config:
    	id-type: assign_id

4.3 多记录操作

  • 按照主键删除多条数据
    在这里插入图片描述
  • 根据主键查询多条记录
    在这里插入图片描述

4.4 逻辑删除

物理删除:业务数据从数据库中丢弃,执行的是delete操作
逻辑删除:为数据设置是否可用状态字段,删除时设置状态字段为不可用状态,数据保留在数据库中,执行的是update操作

实现逻辑删除:

  1. 数据库表中添加逻辑删除标记字段deleted
    MyBatisPlus----基础_第24张图片
  2. 实体类中添加对应字段,并设定当前字段为逻辑删除标记字段
@Data
//@TableName("tbl_user")
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;    //注意id类型是Long
    private String name;
    @TableField(value = "pwd",select = false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist = false)
    private Integer online;
    @TableLogic(value="0",delval="1")
    //value属性是默认值,delval是删除后修改的值
    private Integer deleted;
}

也可以使用全局配置的方式,更加通用:

mybatis-plus:
  global-config:
    db-config:
       #逻辑删除字段名
      logic-delete-field: deleted
       #逻辑删除字面值:未删除为0
      logic-not-delete-value: 0
       #逻辑删除字面值:删除为1
      logic-delete-value: 1
  1. 测试
@Test
void testDelete(){
    userDao.deleteById(1L);
}

执行SQL语句:
在这里插入图片描述

执行数据结果:
MyBatisPlus----基础_第25张图片

4.5 乐观锁

  • 业务并发现象带来的问题:秒杀

  • 假如有100个商品或者票在出售,为了能保证每个商品或者票只能被一个人购买,如何保证不会出现超买或者重复卖

  • 对于这一类问题,其实有很多的解决方案可以使用
    第一个最先想到的就是锁,锁在一台服务器中是可以解决的,但是如果在多台服务器下锁就没有办法控制,比如12306有两台服务器在进行卖票,在两台服务器上都添加锁的话,那也有可能会导致在同一时刻有两个线程在进行卖票,还是会出现并发问题

  • 接下来介绍的这种方式是针对于小型企业的解决方案,因为数据库本身的性能就是个瓶颈,如果对其并发量超过2000以上的就需要考虑其他的解决方案了

  • 简单来说:乐观锁主要解决的问题是当要更新一条记录的时候,希望这条记录没有被别人更新

乐观锁的实现:

  1. 数据库表中添加锁标记字段
    MyBatisPlus----基础_第26张图片

  2. 实体类中添加对应字段,并设定当前字段为乐观锁标记字段

@Data
//@TableName("tbl_user")
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;    //注意id类型是Long
    private String name;
    @TableField(value = "pwd",select = false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist = false)
    private Integer online;
    @TableLogic(value="0",delval="1")
    //value属性是默认值,delval是删除后修改的值
    private Integer deleted;
    @Version
    private Integer version;
}
  1. 配置乐观锁拦截器实现锁机制对应的动态SQL语句拼装
@Configuration
public class MpConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        //1.定义Mp拦截器
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        //分页拦截器
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        //乐观锁拦截器
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return mybatisPlusInterceptor;
    }
}
  1. 使用乐观锁机制在修改前必须先获取到对应数据的version才能正常进行
@Test
void testUpdate(){
    //1.先通过要修改的数据id将当前数据查询出来
    User user = userDao.selectById(3L);     //version=2
    User user2 = userDao.selectById(3L);    //version=2
    user2.setName("Jock aaa");
    userDao.updateById(user2);              //version=>4
    user.setName("Jock bbb");
    userDao.updateById(user);               //verion=3?条件还成立吗?
}

执行的SQL语句:
MyBatisPlus----基础_第27张图片
执行结果:
MyBatisPlus----基础_第28张图片

5 快速开发

5.1 代码生成器(了解)

观察我们之前写的代码,会发现其中也会有很多重复内容,对于这段代码,基本上都是对红色部分的调整,所以我们把去掉红色内容的东西称之为模板,红色部分称之为参数,以后只需要传入不同的参数,就可以根据模板创建出不同模块的dao代码
MyBatisPlus----基础_第29张图片
除了Dao可以抽取模块,其实我们常见的类都可以进行抽取,只要他们有公共部分即可。如下:
MyBatisPlus----基础_第30张图片
分析完后,我们会发现,要想完成代码自动生成,我们需要有以下内容:

  • 模板:MyBatisPlus提供,可以自己提供,但是麻烦,不建议
  • 数据库相关配置:读取数据库获取表和字段信息
  • 开发者自定义配置:手工配置,比如ID生成策略

代码生成器实现:
创建好一个MyBatisPlus模块

  1. 导入对应的jar包mybatis-plus,druid,lombok,代码生成器mybatis-plus-generator和模板引擎velocity-engine-core依赖

<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0modelVersion>
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.7.14-SNAPSHOTversion>
        <relativePath/> 
    parent>
    <groupId>com.zsgroupId>
    <artifactId>mybatisplus_generatorartifactId>
    <version>0.0.1-SNAPSHOTversion>
    <name>mybatisplus_generatorname>
    <description>mybatisplus_generatordescription>
    <properties>
        <java.version>1.8java.version>
    properties>
    <dependencies>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        
        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>mybatis-plus-boot-starterartifactId>
            <version>3.4.1version>
        dependency>

        
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druidartifactId>
            <version>1.1.16version>
        dependency>
        <dependency>
            <groupId>org.mybatis.spring.bootgroupId>
            <artifactId>mybatis-spring-boot-starterartifactId>
            <version>2.3.1version>
        dependency>

        
        <dependency>
            <groupId>com.mysqlgroupId>
            <artifactId>mysql-connector-jartifactId>
            <scope>runtimescope>
        dependency>

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>

        
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <scope>providedscope>
        dependency>

        
        <dependency>
            <groupId>org.apache.velocitygroupId>
            <artifactId>velocity-engine-coreartifactId>
            <version>2.3version>
        dependency>
        <dependency>
            <groupId>org.mybatis.spring.bootgroupId>
            <artifactId>mybatis-spring-boot-starter-testartifactId>
            <version>2.3.1version>
            <scope>testscope>
        dependency>
    dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
            plugin>
        plugins>
    build>
project>

  1. 在引导类包下,创建代码生成类CodeGenerator
public class CodeGenerator {
    public static void main(String[] args) {
        //1.获取代码生成器的对象
        AutoGenerator autoGenerator = new AutoGenerator();
 
        //创建DataSourceConfig 对象设置数据库相关配置
        DataSourceConfig dataSource = new DataSourceConfig();
        dataSource.setDriverName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        autoGenerator.setDataSource(dataSource);
 
        //设置全局配置
        GlobalConfig globalConfig = new GlobalConfig();
        globalConfig.setOutputDir(System.getProperty("user.dir")+"/mybatisplus_04_generator/src/main/java");    //设置代码生成位置
        globalConfig.setOpen(false);    //设置生成完毕后是否打开生成代码所在的目录
        globalConfig.setAuthor("黑马程序员");    //设置作者
        globalConfig.setFileOverride(true);     //设置是否覆盖原始生成的文件
        globalConfig.setMapperName("%sDao");    //设置数据层接口名,%s为占位符,指代模块名称
        globalConfig.setIdType(IdType.ASSIGN_ID);   //设置Id生成策略
        autoGenerator.setGlobalConfig(globalConfig);
 
        //设置包名相关配置
        PackageConfig packageInfo = new PackageConfig();
        packageInfo.setParent("com.aaa");   //设置生成的包名,与代码所在位置不冲突,二者叠加组成完整路径
        packageInfo.setEntity("domain");    //设置实体类包名
        packageInfo.setMapper("dao");   //设置数据层包名
        autoGenerator.setPackageInfo(packageInfo);
 
        //策略设置
        StrategyConfig strategyConfig = new StrategyConfig();
        strategyConfig.setInclude("tbl_user");  //设置当前参与生成的表名,参数为可变参数
        strategyConfig.setTablePrefix("tbl_");  //设置数据库表的前缀名称,模块名 = 数据库表名 - 前缀名  例如: User = tbl_user - tbl_
        strategyConfig.setRestControllerStyle(true);    //设置是否启用Rest风格
        strategyConfig.setVersionFieldName("version");  //设置乐观锁字段名
        strategyConfig.setLogicDeleteFieldName("deleted");  //设置逻辑删除字段名
        strategyConfig.setEntityLombokModel(true);  //设置是否启用lombok
        autoGenerator.setStrategy(strategyConfig);
        //2.执行代码生成器
        autoGenerator.execute();
    }
}

参考文章

你可能感兴趣的:(MyBatisPlus,MyBatisPlus)