微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Mybatis-Plus

目录

@L_404_0@

1.2 配置日志

1.3 测试插入数据

1.4 测试更新数据

雪花算法(插曲)

2.1 核心功能—代码生成器 

2.2 核心功能—条件构造器

简单案例

3.1 拓展—配置主键自增

3.2 拓展—逻辑删除

说明

使用案例

3.3 拓展—自动填充创建时间、修改时间

4.1 插件—分页 

4.2 插件—乐观锁

4.3 插件—性能分析 

使用案例


快速开始 | MyBatis-Plus (baomidou.com)

1.1 快速上手

1.创建数据库,创建表

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

DELETE FROM user;

INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, '[email protected]'),
(2, 'Jack', 20, '[email protected]'),
(3, 'Tom', 28, '[email protected]'),
(4, 'Sandy', 21, '[email protected]'),
(5, 'Billie', 24, '[email protected]');

2.新建一个springboot项目

3.添加依赖

<dependencies>
        <!--数据库驱动-->
        <dependency>
            <groupId>MysqL</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
        </dependency>
        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.1.tmp</version>
        </dependency>

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

4.application.properties

# MysqL 5.xx
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:MysqL://localhost:3306/db2021
spring.datasource.driver-class-name=com.MysqL.jdbc.Driver

5.创建实体类pojo

package com.IT.mybatis.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

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

6.mapper接口

package com.IT.mybatis.mapper;

import com.IT.mybatis.pojo.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;

/**
 * 在对应的mapper上面 继承 基本的类 BaseMapper
 * 所有的CRUD操作都已经编写完成
 * 不需要再像mybatis一样编写映射文件
 */
@Repository
public interface Usermapper extends BaseMapper<User> {
}

7.主启动类

package com.IT.mybatis;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

// 在 SpringBoot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹
@MapperScan("com.IT.mybatis.mapper")
@SpringBootApplication
public class SpringbootMybatisPlusApplication {

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

}

8.测试

package com.IT.mybatis;

import com.IT.mybatis.mapper.UserMapper;
import com.IT.mybatis.pojo.User;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBoottest;

import java.util.List;

@SpringBoottest
class SpringbootMybatisPlusApplicationTests {
    // 继承了BaseMapper,父类中的方法都可以使用,当然,我们也可以编写自己的拓展方法
    @Autowired
    private UserMapper userMapper;

    @Test
    public void testSelect() {
        System.out.println(("----- selectAll method test ------"));
        // UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器 Wrapper,所以不填写就是无任何条件
        List<User> userList = userMapper.selectList(null);
        userList.forEach(System.out::println);
    }

    @Test
    void contextLoads() {
    }

}

测试结果:

User(id=1, name=Jone, age=18, [email protected])
User(id=2, name=Jack, age=20, [email protected])
User(id=3, name=Tom, age=28, [email protected])
User(id=4, name=Sandy, age=21, [email protected])
User(id=5, name=Billie, age=24, [email protected])

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

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

1.2 配置日志

1.在application.properties文件添加

# 配置日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

2.控制台输出结果

Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
Property 'mapperLocations' was not specified.
 _ _   |_  _ _|_. ___ _ |    _ 
| | |\/|_)(_| | |_\  |_)||_|_\ 
     /               |         
                        3.3.1.tmp 
2021-11-20 14:14:06.157  INFO 3972 --- [           main] .m.SpringbootMybatisPlusApplicationTests : Started SpringbootMybatisPlusApplicationTests in 3.45 seconds (JVM running for 4.783)
----- selectAll method test ------
Creating a new sqlSession
sqlSession [org.apache.ibatis.session.defaults.DefaultsqlSession@37c41ec0] was not registered for synchronization because synchronization is not active
2021-11-20 14:14:06.477  INFO 3972 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2021-11-20 14:14:06.778  INFO 3972 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
JDBC Connection [HikariProxyConnection@131532344 wrapping com.MysqL.jdbc.JDBC4Connection@732f6050] will not be managed by Spring
==>  Preparing: SELECT id,name,age,email FROM user 
==> Parameters: 
<==    Columns: id, name, age, email
<==        Row: 1, Jone, 18, [email protected]
<==        Row: 2, Jack, 20, [email protected]
<==        Row: 3, Tom, 28, [email protected]
<==        Row: 4, Sandy, 21, [email protected]
<==        Row: 5, Billie, 24, [email protected]
<==      Total: 5
Closing non transactional sqlSession [org.apache.ibatis.session.defaults.DefaultsqlSession@37c41ec0]

1.3 测试插入数据

1.测试代码

    // 测试插入
    @Test
    public void testInsert(){
        User user=new User(null,"tom",99,"[email protected]");
        int result=userMapper.insert(user);
        System.out.println(result);
        System.out.println(user);
    }

2.插入结果,id自动回填

1
User(id=1461942928325693442, name=tom, age=99, [email protected])

bug修复:在用单元测试的时候,总是向数据库中插入两条数据,如下图

在网上找了解决办法,是这样说的:单元测试时会先build进行编译,而build的时候会执行你项目中的所有加了@Test的方法 并且具体解决步骤如下,将这个勾选上,这是跳过测试的意思

再次用单元测试的时候就可以了

1.4 测试更新数据

1.测试更新代码

    // 测试更新
    @Test
    public void testUpdate(){
        User user=new User();
        user.setId(5L);
        user.setName("hello,mybatis-plus");
        // 参数是一个对象
        int i=userMapper.updateById(user);
        System.out.println(i);
    }

2.测试结果 

雪花算法(插曲)

SNowFlake是Twitter公司采用的一种算法,目的是在分布式系统中产生全局唯一且趋势递增的ID

组成部分(64bit)

1.第一位 占用1bit,其值始终是0,没有实际作用

2.时间戳 占用41bit,精确到毫秒,总共可以容纳约69年的时间

3.工作机器id 占用10bit,其中高位5bit是数据中心ID,低位5bit是工作节点ID,做多可以容纳1024个节点

4.序列号 占用12bit,每个节点每毫秒0开始不断累加,最多可以累加到4095,一共可以产生4096个ID

SNowFlake算法在同一毫秒内最多可以生成多少个全局唯一ID呢:: 同一毫秒的ID数量 = 1024 X 4096 = 4194304 

2.1 核心功能代码生成器 

我写在下面这篇文章里了Mybatis—Plus代码自动生成器超详细讲解(3.5.1+版本)_****^_^****的博客-CSDN博客

2.2 核心功能—条件构造器

通过条件构造器构造复杂sql,过滤、筛选数据

简单案例

1.测试代码

    @Test
    public void testSelect() {
        System.out.println(("----- selectAll method test ------"));
        // UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器 Wrapper,所以不填写就是无任何条件
        QueryWrapper<User>wrapper=new QueryWrapper<>();
        // 查询条件:name不为空、email不为空、年龄大于12
        wrapper.isNotNull("name")
                .isNotNull("email")
                .ge("age",12);
        List<User> userList = userMapper.selectList(wrapper);
        userList.forEach(System.out::println);
    }

2.查询结果

 Preparing: SELECT id,name,age,email,create_time,update_time,version,deleted FROM user 
WHERE deleted=0 AND (name IS NOT NULL AND email IS NOT NULL AND age >= ?) 
==> Parameters: 12(Integer)
<==    Columns: id, name, age, email, create_time, update_time, version, deleted
<==        Row: 5, hello,mybatis-plus, 24, [email protected], null, null, 1, 0
<==        Row: 1461947382575894531, tom, 99, [email protected], null, null, 1, 0
<==        Row: 1461947382575894532, 测试更新, 99, [email protected], 2021-11-20 18:18:03.0, 2021-11-25 15:04:53.0, 1, 0
<==      Total: 3

3.1 拓展—配置主键自增

1.实体类属性添加注解

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    
    @TableId(type = IdType.AUTO)
    private Long id;
    
    private String name;
    private Integer age;
    private String email;
}

2.在数据库连接工具中修改主键为自增

3.再次测试插入 

3.2 拓展—逻辑删除

说明

物理删除:从数据库中直接删除

逻辑删除:实际上只是数据失效,在数据库中没有被删除,当我们查询数据时,如果带上逻辑删除条件,那么被逻辑删除过的数据将不会被查询出来

只对自动注入的sql起效:

  • 插入: 不作限制
  • 查找: 追加where条件过滤掉已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段
  • 更新: 追加where条件防止更新到已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段
  • 删除: 转变为 更新

例如:

  • 删除update user set deleted=1 where id = 1 and deleted=0
  • 查找: select id,name,deleted from user where deleted=0

字段类型支持说明:

附录:

  • 逻辑删除是为了方便数据恢复和保护数据本身价值等等的一种方案,但实际就是删除
  • 如果你需要频繁查出来看就不应使用逻辑删除,而是以一个状态去表示

使用案例

1.数据库增加字段

2.pojo实体类

    @TableLogic // 逻辑删除
    private Integer deleted;

3.application.properties文件

# 配置逻辑删除
# 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
# mybatis-plus.global-config.db-config.logic-delete-field=deleted
# 逻辑已删除值(认为 1)
mybatis-plus.global-config.db-config.logic-delete-value=1
# 逻辑未删除值(认为 0)
mybatis-plus.global-config.db-config.logic-not-delete-value=0

4.测试代码

    // 测试逻辑删除
    @Test
    public void testDeleteBatchId(){
        userMapper.deleteBatchIds(Arrays.asList(3L,4L));
    }

5.数据库结果,deleted字段值变为1

6. 逻辑删除下,测试查询操作时会自动拼接查询条件,deleted=1的数据将不会被查询出来

==>  Preparing: SELECT id,name,age,email,create_time,update_time,version,deleted FROM user WHERE deleted=0 
==> Parameters: 
<==    Columns: id, name, age, email, create_time, update_time, version, deleted
<==        Row: 5, hello,mybatis-plus, 24, [email protected], null, null, 1, 0
<==        Row: 1461947382575894531, tom, 99, [email protected], null, null, 1, 0
<==        Row: 1461947382575894532, 更新了数据, 99, [email protected], 2021-11-20 18:18:03.0, 2021-11-20 18:28:47.0, 1, 0

3.3 拓展—自动填充创建时间、修改时间

根据阿里巴巴开发手册,在数据库表中,对数据的创建、修改操作要求记录下创建时间、修改时间,并且由于数据量庞大,手工添加肯定是不行的,肯定需要自动填充,下面我们来看怎么实现这个功能

1.在表中新增字段:create_time、update_time

2.实体类:注解填充字段 @TableField(.. fill = FieldFill.INSERT)

package com.IT.mybatis.pojo;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    @TableId(type = IdType.AUTO)
    private Long id;

    private String name;
    private Integer age;
    private String email;
    // 字段添加填充内容
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
}

3.自定义实现类MyMetaObjectHandler并继承MetaObjectHandler

package com.IT.mybatis.handler;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    // 插入时的填充策略
    @Override
    public void insertFill(MetaObject MetaObject) {
        log.info("start insert fill ....");
        this.strictInsertFill(MetaObject, "createTime", LocalDateTime.class, LocalDateTime.Now()); // 起始版本 3.3.0(推荐使用)
    }
    // 更新时的填充策略
    @Override
    public void updateFill(MetaObject MetaObject) {
        log.info("start update fill ....");
        this.strictUpdateFill(MetaObject, "updateTime", LocalDateTime.class, LocalDateTime.Now()); // 起始版本 3.3.0(推荐)
    }
}

4.测试插入操作

    @Test
    public void testInsert() {
        User user = new User((Long)null, "tom", 99, "[email protected]", (LocalDateTime)null, (LocalDateTime)null);
        int result = this.userMapper.insert(user);
        System.out.println(result);
        System.out.println(user);
    }

结果:

1
User(id=1461947382575894532, name=tom, age=99, [email protected], createTime=2021-11-20T18:18:03.382, updateTime=null)

刷新数据库

5.测试更新操作

    // 测试更新
    @Test
    public void testUpdate(){
        User user=new User();
        user.setId(1461947382575894532L);
        user.setName("更新了数据");
        // 参数是一个对象
        int i=userMapper.updateById(user);
        System.out.println(i);
    }

后台输出1,更新成功!然后看一下数据库

注意事项:

  • 填充原理是直接给entity属性设置值!!!
  • 注解则是指定该属性在对应情况下必有值,如果无值则入库会是null
  • MetaObjectHandler提供的方法的策略均为:如果属性有值则不覆盖,如果填充值为null则不填充
  • 字段必须声明TableField注解,属性fill选择对应策略,该声明告知Mybatis-Plus需要预留注入sql字段
  • 填充处理器MyMetaObjectHandler在 Spring Boot 中需要声明@Component@Bean注入
  • 要想根据注解FieldFill.xxx字段名以及字段类型来区分必须使用父类strictInsertFill或者strictUpdateFill方法
  • 不需要根据任何来区分可以使用父类fillStrategy方法 

4.1 插件分页 

PaginationInnerInterceptor

支持数据库

  • MysqL,oracle,db2,h2,hsqlsqlite,postgresqlsqlserver,Phoenix,Gauss ,clickhouse,Sybase,OceanBase,Firebird,cubrid,goldilocks,csiidb

1.在配置类中添加组件

    // 旧版
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        // 设置请求的页面大于最大页后操作, true 调回到首页;false 继续请求 
        // 认是false
        // paginationInterceptor.setoverflow(false);
        // 设置最大单页限制数量认 500 条,-1 不受限制
        // paginationInterceptor.setLimit(500);
        // 开启 count 的 join 优化,只针对部分 left join
        paginationInterceptor.setCountsqlParser(new JsqlParserCountOptimize(true));
        return paginationInterceptor;
    }

2.测试代码

    // 测试分页查询
    @Test
    public void testPage(){
        // 页数:1,每页数据条数:5
        Page<User>page=new Page<>(1,5);
        userMapper.selectPage(page,null);
        page.getRecords().forEach(System.out::println);
    }

3.测试结果

<==        Row: 1, 22222211, 18, [email protected], null, 2021-11-20 19:00:27.0, 4
<==        Row: 2, Jack, 20, [email protected], null, null, 1
<==        Row: 3, Tom, 28, [email protected], null, null, 1
<==        Row: 4, Sandy, 21, [email protected], null, null, 1
<==        Row: 5, hello,mybatis-plus, 24, [email protected], null, null, 1

查询第二页

    // 测试分页查询
    @Test
    public void testPage(){
        // 页数:2,每页数据条数:5
        Page<User>page=new Page<>(2,5);
        userMapper.selectPage(page,null);
        page.getRecords().forEach(System.out::println);
    }

查询结果 

<==        Row: 1461947382575894531, tom, 99, [email protected], null, null, 1
<==        Row: 1461947382575894532, 更新了数据, 99, [email protected], 2021-11-20 18:18:03.0, 2021-11-20 18:28:47.0, 1
<==      Total: 2

4.2 插件—乐观锁

OptimisticLockerInnerInterceptor

当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:

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

乐观锁配置步骤

1.表中增加version字段

2.实体类

    @Version // 乐观锁注解
    private Integer version;

说明:

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

3.注册组件

package com.IT.mybatis.config;

import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
// 配置类
@Configuration
// 在 SpringBoot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹
@MapperScan("com.IT.mybatis.mapper")
public class MyBatisPlusConfig {
    // 注册乐观锁插件
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }
}

4.测试乐观锁

    // 测试乐观锁
    @Test 
    public void testOptimisticLocker(){
        // 1.查询用户信息
        User user=userMapper.selectById(1L);
        // 2.修改用户信息
        user.setName("zhangsan");
        user.setEmail("[email protected]");
        // 3.执行更新操作
        userMapper.updateById(user);
    }

可以看到更新成功

同时我们可以模拟一下乐观锁更新失败的情况

    // 测试乐观锁
    @Test
    public void testOptimisticLocker(){
        // 线程1
        // 查询用户信息
        User user1=userMapper.selectById(1L);
        // 修改用户信息
        user1.setName("111111");
        user1.setEmail("[email protected]");
        
        // 线程2
        // 查询用户信息
        User user2=userMapper.selectById(1L);
        // 修改用户信息
        user2.setName("222222");
        user2.setEmail("[email protected]");
        
        // 执行更新操作: 本来是1先执行的,但是这时候被2抢先了
        userMapper.updateById(user2);
        userMapper.updateById(user1);
    }

1执行更新操作时的后台日志

==>  Preparing: UPDATE user SET name=?, age=?, email=?, update_time=?, version=? WHERE id=? AND version=? 
==> Parameters: 111111(String), 18(Integer), [email protected](String), 2021-11-20T19:00:27(LocalDateTime), 3(Integer), 1(Long), 2(Integer)
<==    Updates: 0

数据库中可以看到2更新成功了,而1去执行更新操作的时候,由于版本号被修改,版本对应不上,导致更新失败! 

4.3 插件性能分析 

性能分析拦截器,用于输出每条sql语句及其执行时间

新版本中推荐使用第三方拓展 执行sql分析打印 功能

使用案例

1.引入依赖

2.application.yml

  • 关键点1:url需要加上p6spy 固定的是jdbc:p6spy:数据库名…...
  • 关键点2:driver-class-name指定的是p6spy的驱动
spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:p6spy:MysqL://localhost:3306/db2021
    driver-class-name: com.p6spy.engine.spy.P6SpyDriver

3.spy.properties

  • 关键点,一定要通过driverlist指定真实的JDBC驱动
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logmessageformat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系统记录 sql
# appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 设置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 实际驱动可多个
# driverlist=org.h2.Driver
# 是否开启慢sql记录
outagedetection=true
# 慢sql记录标准 2 秒
outagedetectioninterval=2
# 真实JDBC driver
driverlist=com.MysqL.jdbc.Driver

4.测试

再测试一下更新操作的用时

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐