2023年11月26日发(作者:)

MyBatis-Plus(概述、快速⼊门、⽇志配置、主键⽣成策略、⾃动填充、

CRUD、性能。。。

⽂章⽬录

MyBatis-Plus

(⼀)概述

1. MyBatis-Plus(简称 MP)是⼀个 MyBatis 的增强⼯具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提⾼效率⽽⽣;是

MyBatis 最好的搭档,就像魂⽃罗中的 1P、2P,基友搭配,效率翻倍!

2. 官⽹地址:

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, 'test1@'),

(2, 'Jack', 20, 'test2@'),

(3, 'Tom', 28, 'test3@'),

(4, 'Sandy', 21, 'test4@'),

(5, 'Billie', 24, 'test5@');

2. 创建SpringBoot项⽬导⼊项⽬依赖

<dependencies>

<dependency>

<groupId>groupId>

<artifactId>spring-boot-starterartifactId>

dependency>

<dependency>

<groupId>groupId>

<artifactId>spring-boot-starter-testartifactId>

<scope>testscope>

dependency>

<dependency>

<groupId>mysqlgroupId>

<artifactId>mysql-connector-javaartifactId>

dependency>

<dependency>

<groupId>tlombokgroupId>

<artifactId>lombokartifactId>

<optional>trueoptional>

dependency>

<dependency>

<groupId>ougroupId>

<artifactId>mybatis-plus-boot-starterartifactId>

<version>3.0.5version>

dependency>

dependencies>

3. 配置 ties

注意 mysql 5 和 mysql 8 的配置区别:mysql 8 需要时区配置

serverTimezone

me=root

rd=123456

=jdbc:mysql://localhost:3306/数据库名?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8

-class-name=

4. 主体测试代码

1. pojo层

@Data

public class User {

private Long id;

private String name;

private Integer age;

private String email;

}

2. mapper层

@Repository

public interface UserMapper extends BaseMapper<User> {

}

到这⾥基本的CRUD代码已经编写完成!!

3. 启动类

@SpringBootApplication

@MapperScan("")

public class MybatisPlusApplication {

public static void main(String[] args) {

SpringApplication.run(MybatisPlusApplication.class, args);

}

}

4. 测试

@SpringBootTest

class MybatisPlusApplicationTests {

@Autowired

private UserMapper userMapper;

@Test

void contextLoads() {

List<User> list = userMapper.selectList(null);

list.forEach(System.out::println);

}

}

5. 结果

MyBatis-Plus帮我们写了sql、还有⼀些⽅法!!

(三)⽇志输出

1. 配置

-impl=Impl

2. 结果

(四)基本操作测试

1. 数据插⼊测试&雪花算法

插⼊数据:

@Test

public void testInsert(){

User user = new User();

user.setName("zhangsan");

user.setAge(10);

user.setEmail("123456@");

int result = userMapper.insert(user);

System.out.println(result);

System.out.println(user);

}

结果:我们并没有定义id,却⽣成了⼀长串数字!

为什么会⾃动⽣成id?

原因:MyBatis-Plus 会采⽤主键⽣成策略,这⾥采⽤了 Twitter 的 snowflake 算法(雪花算法)

2. 主键⽣成策略

2.1 主键⾃增

在主键上增加注解

@Data

public class User {

@TableId(type = IdType.AUTO)

private Long id;

}

保证数据库的主键为⾃增

我么可以发现⾃动增量为: 1256212

测试

@Test

public void testInsert(){

User user = new User();

user.setName("zhangsan");

user.setAge(10);

user.setEmail("123456@");

int result = userMapper.insert(user);

System.out.println(result);

System.out.println(user);

}

结果

2.2 注解TableId

我们进⼊ TableId 源码查看:发现IdType

public @interface TableId {

String value() default "";

IdType type() default IdType.NONE;

}

进⼊ IdType源码:

public enum IdType {

AUTO(0), //

主键⾃增

NONE(1), //

未设置主键

INPUT(2), // idid=null

⼿动输⼊:必须⾃⼰⼿动设置,不然

ID_WORKER(3), // id

默认的全局唯⼀

UUID(4), // uuid

全局唯⼀

ID_WORKER_STR(5); // ID_WORKER

字符串表⽰

}

3. 更新数据操作

将 id 属性的注解设置为 ,在插⼊⼀条数据⽤来更改。

@TableId(type = )

//

插⼊数据

@Test

public void testInsert2(){

User user = new User();

user.setId(6L);

user.setName("zhangsan");

user.setAge(20);

user.setEmail("123456@");

int result = userMapper.insert(user);

}

更新数据:

@Test

public void updateTest(){

User user = new User();

user.setId(6L);

user.setName("lisi");

int result = userMapper.updateById(user);

}

发现:⽅法需要的参数类型是⼀个对象;并且⾃动拼接了动态sql:

updateById

4. ⾃动填充

我们新增两个属性:,但不设置默认值和更新属性!

create_timeupdate-time

在pojo层:标记为填充字段

@TableField(fill = FieldFill.INSERT)

private Date createTime;

@TableField(fill = FieldFill.INSERT_UPDATE)

private Date updateTime;

FieldFill

属性源码:

public enum FieldFill {

DEFAULT, //

默认不处理

INSERT, //

插⼊填充字段

UPDATE, //

更新填充字段

INSERT_UPDATE; //

插⼊和更新填充字段

}

新建⼀个 Handler 包,创建实现类:填充处理器MyHandler在 Spring Boot 中需要声明

@Component

@Slf4j

@Component

public class MyHandler implements MetaObjectHandler {

@Override

public void insertFill(MetaObject metaObject) {

log.info("start insert fill ....");

this.setFieldValByName("createTime",new Date(),metaObject);

this.setFieldValByName("updateTime",new Date(),metaObject);

}

@Override

public void updateFill(MetaObject metaObject) {

log.info("start update fill ....");

this.setFieldValByName("updateTime",new Date(),metaObject);

}

}

测试:

//

插⼊操作

@Test

public void testInsert(){

User user = new User();

user.setName("test1");

user.setAge(10);

user.setEmail("123456@");

int result = userMapper.insert(user);

System.out.println(result);

System.out.println(user);

}

//

更新操作

@Test

public void updateTest(){

User user = new User();

user.setId(6L);

user.setName("lisi");

int result = userMapper.updateById(user);

}

结果:mybatis-plus 会⾃动帮我们填充!

5. 查询操作

1. 查询测试:查询 的user

id=1

@Test

public void selectTest1(){

User user = userMapper.selectById(1L);

System.out.println(user);

}

结果:

2. 批量查询:插叙 id = 1、2、3 的user

@Test

public void selectTest2(){

List<User> users= userMapper.selectBatchIds(Arrays.asList(1,2,3));

users.forEach(System.out::println);

}

结果:

3. 条件查询:查询 的user

name=zhangsan AND age=10

@Test

public void selectTest3(){

HashMap<String, Object> map = new HashMap<>();

// map

通过⾃定义条件

map.put("name","zhangsan");

map.put("age",10);

List<User> users = userMapper.selectByMap(map);

users.forEach(System.out::println);

}

结果:

6. 分页查询

1. 配置分页插件

@Bean

public PaginationInterceptor paginationInterceptor() {

return new PaginationInterceptor();

}

2. 测试:每页三条数据查询

@Test

public void testPage(){

//

参数:当前页,页数⼤⼩

Page<User> page = new Page<>(1,3);

userMapper.selectPage(page,null);

page.getRecords().forEach(System.out::println);

}

3. 结果:sql语句还是使⽤了 Limit

7. 删除操作

@Test

public void deleteTest(){

// idid=1user

通过删除:删除

userMapper.deleteById(1L);

// 123user

批量删除:删除

userMapper.deleteBatchIds(Arrays.asList(1,2,3));

// name=zhangsan

条件删除:删除的对象

HashMap<String, Object> map = new HashMap<>();

map.put("name","zhangsan");

userMapper.deleteByMap(map);

}

8. 逻辑删除

1. 物理删除:从数据库中直接移除数据;

逻辑删除:数据库中并没有移除数据,⽽是通过⼀个变量使数据失效(deleted=0、deleted=1);管理员可以查看删除记录,类似

于回收站!

2. 测试

在表新增 字段,默认值为0;

deleted

在实体类中新增属性:

@TableLogic

private Integer deleted;

配置类

//

逻辑删除

@Bean

public ISqlInjector iSqlInjector(){

return new LogicSqlInjector();

}

配置

# (1)

逻辑已删除值默认为

mybatis----delete-value=1

mybatis----not-delete-value=0

测试:删除1号user

@Test

public void logicalDeleteTest() {

userMapper.deleteById(1L);

}

结果:数据库1号user还存在,sql语句是update,将deleted字段更新为1

我们再来查询⼀下1号user:

@Test

public void selectTest1(){

User user = userMapper.selectById(1L);

System.out.println(user);

}

结果:已经查询不到1号user!

9. 乐观锁操作

1. 适⽤场景:更新的数据是最新的,即这条数据没有被别⼈更新。

2. 实现⽅式:

取出记录时,获取当前 version;

更新时,带上这个 version;

执⾏更新时,

set version = newVersion where version = oldVersion

如果 version 不对,就更新失败。

3. 测试:

在表中添加 字段,默认值为1;

version

实体类:

@Version

private Integer version;

新增⼀个配置类:

@EnableTransactionManagement

@Configuration

public class MybatisPlusConfig {

//

注册乐观锁插件配置

@Bean

public OptimisticLockerInterceptor optimisticLockerInterceptor() {

return new OptimisticLockerInterceptor();

}

}

测试乐观锁:

测试前的表:

乐观锁成功:执⾏⼀次updateById

@Test

public void OptimisticLockerTest1(){

User user = userMapper.selectById(1L);

user.setName("aaa");

userMapper.updateById(user);

}

结果:数据更新成功,version 由 1 更新为 2

乐观锁失败:模拟两条线程去更新同⼀条数据

@Test

public void OptimisticLockerTest2(){

// 1

线程

User user1 = userMapper.selectById(1L);

user1.setName("bbb");

// 2

线程

User user2 = userMapper.selectById(1L);

user2.setName("ccc");

userMapper.updateById(user2);

userMapper.updateById(user1);

}

结果:由于乐观锁的存在,数据更新为user2,user1更新失败,version更新为 3 ,如果没有乐观锁,会覆盖user2的值!

(五)性能分析插件

⽤于输出每条 SQL 语句及其执⾏时间,⽐如超过某个时间就会停⽌操作!使⽤此插件可以帮助我们提升效率!

1. 配置

@Bean

// dev test

设置环境开启

@Profile({"dev","test"})

public PerformanceInterceptor performanceInterceptor() {

PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();

performanceInterceptor.setMaxTime(100); // sql

设置语句执⾏的最⼤时间,超过这个时间就不执⾏

performanceInterceptor.setFormat(true); //

设置格式化

return performanceInterceptor;

}

在ties中,设置开发环境:

=dev

2. 测试

查询user结果:

@Test

void contextLoads() {

List<User> list = userMapper.selectList(null);

list.forEach(System.out::println);

}

结果:

(六)条件构造器

⽤来操作⼀些复杂查询的构造器!

1. isNotNullgegtltle

isNotNull:不为空

ge:⼤于等于

gt:⼤于

lt:⼩于

le:⼩于等于

如查询user,条件:

name不为空 AND email不为空 AND age⼤于等于20

@Test

void test1() {

QueryWrapper<User> wrapper = new QueryWrapper<>();

wrapper

.isNotNull("name")

.isNotNull("email")

.ge("age",20);

List<User> list = userMapper.selectList(wrapper);

list.forEach(System.out::println);

}

结果:

2. eq

eq:等于

如:查询user姓名为 的⽤户

Jack

@Test

void test2(){

QueryWrapper<User> wrapper = new QueryWrapper<>();

wrapper.eq("name","Jack");

User user = userMapper.selectOne(wrapper);

System.out.println(user);

}

结果:

3. between

between:值1 AND 值2

如:查询20~30岁之间的⽤户数量

@Test

void test3(){

QueryWrapper<User> wrapper = new QueryWrapper<>();

wrapper.between("age",20,30);

Integer count = userMapper.selectCount(wrapper);

System.out.println(count);

}

结果:

4. 模糊查询

like

查询name中有王字的name:—>

like("name", "")name like '%%'

notLike

查询name中没有王字的name:—>

notLike("name", "")name not like '%%'

likeLeft

从name的左边开始匹配含有王字的name,—>

likeLeft("name", "")name like '%'

likeRight

从name的右边开始匹配含有王字的name,—>

likeRight("name", "")name like '%'

5. inSql

⼦查询,字段 IN ( sql语句 )

@Test

void test4(){

QueryWrapper<User> wrapper = new QueryWrapper<>();

wrapper.inSql("id","select id from user where id<3");

List<Object> list = userMapper.selectObjs(wrapper);

list.forEach(System.out::println);

}

结果:

6. orderByAscorderByDesc

orderByAsc:降序查询

orderByDesc:升序查询

@Test

void test5(){

QueryWrapper<User> wrapper = new QueryWrapper<>();

wrapper.orderByAsc("id");

List<Object> list = userMapper.selectObjs(wrapper);

list.forEach(System.out::println);

}

结果:

(七)代码⽣成器

通过 AutoGenerator 可以快速⽣成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极⼤的提升了开发效

率!!

1. 新建⼀个SpringBoot项⽬

我们并没有创建entity、mapper、service…包。

2. 编写代码⽣成器

import com.baomidou.mybatisplus.annotation.DbType;

import com.baomidou.mybatisplus.annotation.FieldFill;

import com.baomidou.mybatisplus.annotation.IdType;

import com.baomidou.mybatisplus.annotation.TableField;

import com.baomidou.mybatisplus.generator.AutoGenerator;

import com.baomidou.mybatisplus.generator.config.DataSourceConfig;

import com.baomidou.mybatisplus.generator.config.GlobalConfig;

import com.baomidou.mybatisplus.generator.config.PackageConfig;

import com.baomidou.mybatisplus.generator.config.StrategyConfig;

import com.baomidou.mybatisplus.generator.config.po.TableFill;

import com.baomidou.mybatisplus.generator.config.rules.DateType;

import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;

import java.util.ArrayList;

public class CodeGenerator {

public static void main(String[] args) {

//

代码⽣成器

AutoGenerator mpg = new AutoGenerator();

/**

*

全局配置

*/

GlobalConfig gc = new GlobalConfig();

//

获得实体类

String projectPath = System.getProperty("");

//

设置输出路径

gc.setOutputDir(projectPath + "/src/main/java");

//

设置作者

gc.setAuthor("");

//

是否覆盖

gc.setOpen(false);

// ServiceI

去除前缀

gc.setServiceName("%sService");

// id

设置的类型

gc.setIdType(IdType.ID_WORKER);

//

设置时间类型

gc.setDateType(DateType.ONLY_DATE);

// SwaggerSwagger

设置⽂档⽣成⼯具:当然需要导⼊的依赖

gc.setSwagger2(true);

mpg.setGlobalConfig(gc);

/**

*

数据源配置

*/

DataSourceConfig dsc = new DataSourceConfig();

dsc.setUrl("jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8");

dsc.setDriverName("");

dsc.setUsername("root");

dsc.setPassword("123456");

dsc.setDbType(DbType.MYSQL);

mpg.setDataSource(dsc);

/**

*

包配置

* */

PackageConfig pc = new PackageConfig();

pc.setModuleName("study");

pc.setParent("");

pc.setEntity("entity");

pc.setMapper("mapper");

pc.setController("controller");

pc.setService("service");

mpg.setPackageInfo(pc);

/**

*

策略配置

* */

StrategyConfig strategy = new StrategyConfig();

//

设置映射的表

strategy.setInclude("user");

//

下划线转驼峰命名

strategy.setNaming(NamingStrategy.underline_to_camel);

strategy.setColumnNaming(NamingStrategy.underline_to_camel);

// lombok

开启

strategy.setEntityLombokModel(true);

strategy.setLogicDeleteFieldName("deleted");

//

⾃动填充配置

TableFill createTime = new TableFill("create_time", FieldFill.INSERT);

TableFill updateTime = new TableFill("update_time", FieldFill.INSERT);

ArrayList<TableFill> list = new ArrayList<>();

list.add(createTime);

list.add(updateTime);

strategy.setTableFillList(list);

//

乐观锁配置

//

乐观锁配置

strategy.setVersionFieldName("version");

// controller

层配置

strategy.setRestControllerStyle(true);

strategy.setControllerMappingHyphenStyle(true);//

跳转地址变为下划线连接格式

mpg.setStrategy(strategy);

mpg.execute();

}

}

3. 测试结果

包和代码全部⾃动⽣成!!后⾯要⽣成其他对应的代码,只需要改数据库名即可!!

我们查看实体类:这全是mybatis-plus的代码⽣成器帮我们实现的

import com.baomidou.mybatisplus.annotation.IdType;

import java.util.Date;

import com.baomidou.mybatisplus.annotation.Version;

import com.baomidou.mybatisplus.annotation.TableId;

import com.baomidou.mybatisplus.annotation.FieldFill;

import com.baomidou.mybatisplus.annotation.TableLogic;

import com.baomidou.mybatisplus.annotation.TableField;

import java.io.Serializable;

import io.swagger.annotations.ApiModel;

import io.swagger.annotations.ApiModelProperty;

import lombok.Data;

import lombok.EqualsAndHashCode;

import lombok.experimental.Accessors;

/**

*

*

⽤户类

*

*

* @author

* @since 2020-05-05

*/

@Data

@EqualsAndHashCode(callSuper = false)

@Accessors(chain = true)

@ApiModel(value="User对象", description="")

public class User implements Serializable {

private static final long serialVersionUID = 1L;

@ApiModelProperty(value = "主键ID")

@TableId(value = "id", type = IdType.AUTO)

private Long id;

@ApiModelProperty(value = "姓名")

private String name;

@ApiModelProperty(value = "年龄")

private Integer age;

@ApiModelProperty(value = "邮箱")

private String email;

@ApiModelProperty(value = "创建时间")

@TableField(fill = FieldFill.INSERT)

private Date createTime;

@ApiModelProperty(value = "更新时间")

@TableField(fill = FieldFill.INSERT)

private Date updateTime;

@ApiModelProperty(value = "乐观锁")

@Version

private Integer version;

@TableLogic

private Integer deleted;

}

//下篇再见…谢谢