概述
文章目录
- 博客资料参考官方文档
- 数据库创建和数据添加
- 创建数据库
- 创建数据表
- 数据表数据录入
- 创建SpringBoot项目
- 主要依赖引入
- 配置文件编写
- 编写POJO对象类
- 编写mapper类
- 修改启动类,增加mapper扫描
- 编写测试类
- 增加日志打印配置
- 简单CRUD操作
- 插入数据 insert
- 主键生成策略
- IdType.AUTO 主键自增
- IdType.ID_WORKER 雪花算法
- IdType.INPUT 手动输入
- 更新 update
- 自动填充
- 方式一:数据库方式(了解)
- 方式二:代码方式(推荐)
- 乐观锁
- 实现步骤
- 查询 select
- 查询单个用户信息
- 查询多个用户信息
- 使用map封装条件,进行查询
- 分页查询
- 删除 delete
- 普通删除
- 删除指定id的信息
- 批量删除
- 条件删除
- 逻辑删除
- 条件构造器
- eq、allEq、ne
- 大小区分
- 范围查询
- 模糊查询
- isNull、isNotNull
- in、notIn、inSql
- 排序
- 性能分析插件
- `3.2.0 版本之前`
- `3.2.0 版本之后`
- 代码下载
博客资料参考官方文档
https://mp.baomidou.com/guide/
数据库创建和数据添加
创建数据库
新建数据库,并给数据库命令、选择字符集等。
创建数据表
数据表采取官方给定的为准:
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)
);
数据表数据录入
INSERT INTO user (id, name, age, email) VALUE
(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');
相比起官方给定的sql,将values
更换为value
。
多条记录使用value,而不是values!
创建SpringBoot项目
主要依赖引入
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
【注意:】不要将 mybatis 和 mybatis-plus 两个依赖同时导入!
版本和兼容性问题。
mybatis-plus 不是官方的,是大佬自己写的。
springboot使用的版本如下所示:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
配置文件编写
主要是写服务的端口、数据库连接等。
这里需要注意几点:
-
MySQL 8以前:
驱动使用:com.mysql.jdbc.Driver
。 -
MySQL 8以后:
驱动使用:com.mysql.cj.jdbc.Driver
需要增加时区
。
【标注:】驱动具有向下兼容的特性,如果使用高版本的驱动,一定注意时区的限制
。
本次使用
高版本
。
所以Springboot项目
的配置文件编写如下所示:
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://192.168.99.100:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
server.port=80
编写POJO对象类
编写一个pojo
的对象类,实现数据的接收映射。如下所示:
package cn.linkpower.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;
}
编写mapper类
使用mybatis-plus
插件,针对简单的CRUD
操作,不需要额外的编写对他应的sql,或者说编写xml文件,只需要将对应的mapper
接口继承
一个BaseMapper
即可。如下所示:
package cn.linkpower.mapper;
import cn.linkpower.pojo.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository // 表示这个接口是持久层;也可以换成一个 @Mapper 注解
public interface UserMapper extends BaseMapper<User> {
}
修改启动类,增加mapper扫描
由于没有配置mapper
的自动扫描路径,可以直接在启动类上增加一个@MapperScan
实现自动扫描装载操作。如下所示:
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("cn.linkpower.mapper")
public class SpringbootMybatisApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootMybatisApplication.class, args);
}
}
编写测试类
在/test/
目录下,编写对应的测试类即可,如下所示:
查询所有的用户信息。
package cn.linkpower;
import cn.linkpower.mapper.UserMapper;
import cn.linkpower.pojo.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootMybatisApplicationTest {
@Autowired
private UserMapper userMapper;
@Test
public void getUserInfo(){
List<User> users = userMapper.selectList(null);
users.stream().forEach(System.out::println);
}
}
执行后发现控制台打印数据如下:
增加日志打印配置
如果想查看对应自动生成的sql
,则需要在配置文件中增加如下配置:
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
重新执行测试类,观察控制台输出:
日志打印出来了!
上面的基本配置已经实现了不需要编写sql
就能达到简单的 CRUD
操作。接下来具体分析各种操作方式的应用。
简单CRUD操作
插入数据 insert
在BaseMapper<T>
父类中,存在新增操作的接口,只需要调用即可。如下所示的测试操作:
@Test
public void insertTest(){
User user = new User();
user.setName("香蕉");
user.setAge(22);
user.setEmail("123456@");
int insert = userMapper.insert(user);
System.out.println("-->"+insert); // 受影响的行数
System.out.println("-->"+user); // id 自动回填
}
执行操作执行后,查看控制台和数据库数据信息。如下所示:
【发现:】随机生辰的 id 居然是 0 !
再次执行一下上述的代码,直接报错!
所以,现在的问题根源在于主键生成的策略问题,接下来说明下主键生成策略
有哪些。
主键生成策略
在之前编写的pojo.User
类中,原内容如下所示:
package cn.linkpower.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;
}
常见的能作为主键id的有以下几种类型:
自增id、UUID、雪花算法、redis生成、Zookeper生成等。
参考资料:分布式系统唯一ID生成方案汇总
在pojo
指定的类中,我们可以对其主键列增加新的注解@TableId
,其中存在以下几个属性:
其中对应的类型为IdType
是一个enum 枚举类
,其中各项参数信息如下所示:
public enum IdType {
AUTO(0), // 数据库 id 自增
NONE(1), // 未设置主键
INPUT(2), // 手动输入
ID_WORKER(3), // 全局id
UUID(4), // 全局唯一id uuis
ID_WORKER_STR(5); // 截取字段串,ID_WORKER的字符串表示法
private int key;
private IdType(int key) {
this.key = key;
}
public int getKey() {
return this.key;
}
}
接下来我们将每个参数的含义做一个代码测试:
IdType.AUTO 主键自增
使用@TableId(type = IdType.AUTO)
针对主键字段
增加自增策略
,需要保证以下几点:
- 对应的类的主键上增加注解:
@TableId(type = IdType.AUTO)
- 对应数据库的主键必须是自增的。
package cn.linkpower.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
@TableId(type = IdType.AUTO)
private long id;
private String name;
private Integer age;
private String email;
}
执行新增操作测试代码,查看控制台日志如下所示:
IdType.ID_WORKER 雪花算法
关于雪花算法,可以参考博客分布式系统唯一ID生成方案汇总。
snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。
修改对应类中的主键生成策略类型:
@TableId(value = "id",type = IdType.ID_WORKER)
private Long id;
【注意:】这里的类型由 long 更换为 包装类型 Long !
long 的默认值为 0,当数据库无id为0的数据信息时,可以增加成功!
如果存在。则会出现报错信息!
原因在于,mybatis plus
源码中的MybatisDefaultParameterHandler
中存在一个校验。
其次将数据库中的主键类型选择不自增
!
设置自增或不自增都可以!
测试代码执行,控制台和数据库数据如下所示:
IdType.INPUT 手动输入
如果主键类型选用INPUT
方式,则需要在insert
数据时,自己指定id信息
。
数据库
主键不自增
!
当不指定时:
@Test
public void insertTest(){
User user = new User();
user.setName("香蕉 input 3");
user.setAge(22);
user.setEmail("123456@");
int insert = userMapper.insert(user);
System.out.println("-->"+insert);
System.out.println("-->"+user);
}
控制台中则会出现报错:
手动设置主键id的值:
@Test
public void insertTest(){
User user = new User();
user.setId(66l);
user.setName("香蕉 input 3");
user.setAge(22);
user.setEmail("123456@");
int insert = userMapper.insert(user);
System.out.println("-->"+insert);
System.out.println("-->"+user);
}
更新 update
在mybatis plus 中,针对插入操作也提供了相应的处理方式。
测试代码如下所示:
@Test
public void testUpdate(){
User user = new User();
user.setId(1l); // id为1
user.setName("香蕉"); // 数据库原信息为 Jone ,此时需要将其name更改为 “香蕉”
int updateNum = userMapper.updateById(user);
System.out.println("--->"+updateNum);
}
其中,测试的User.java如下所示:
package cn.linkpower.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
@TableId(value = "id",type = IdType.ID_WORKER)
private Long id;
private String name;
private Integer age;
private String email;
}
执行后控制台数据信息如下所示:
如果是多个字段呢?
修改测试代码如下所示:
@Test
public void testUpdate(){
User user = new User();
user.setId(1l); // id为1
user.setName("香蕉222"); // 数据库原信息为 “香蕉” ,此时需要将其name更改为 “香蕉222”
user.setAge(55); // 原数据为 18
int updateNum = userMapper.updateById(user);
System.out.println("--->"+updateNum);
System.out.println("--->"+user);
}
这就是一个简单的
动态sql
!
自动填充
在实际项目中,针对比如时间类型的数据信息,需要保证自动填充
的方式动态的增加或修改敌营的数据信息。此时就需要使用到自动填充
操作。
首先需要在对应的数据库表中新增字段:create_time、update_time
方式一:数据库方式(了解)
【扩展:】
如果想依靠数据库自动添加数据,可以采取如下方式。(本次不采用此方式
)
需要给其设定默认值。
CURRENT_TIMESTAMP
增加实体映射类的属性:
package cn.linkpower.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
@TableId(value = "id",type = IdType.ID_WORKER)
private Long id;
private String name;
private Integer age;
private String email;
// 注意驼峰
private Date createTime;
private Date updateTime;
}
执行数据添加操作:
@Test
public void insertTest(){
User user = new User();
//user.setId(66l); // type = IdType.INPUT
user.setName("香蕉 ");
user.setAge(28);
user.setEmail("123456@");
int insert = userMapper.insert(user);
System.out.println("-->"+insert);
System.out.println("-->"+user);
}
【遗留问题:】
这里测试,数据库的更新操作能成功执行,但是时间并未变更。
方式二:代码方式(推荐)
数据库中只进行字段的增加,不设定默认值
。
官方文档参考连接:
https://mp.baomidou.com/guide/auto-fill-metainfo.html
修改对应的User.java类,增加注解标识
。
package cn.linkpower.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.io.Serializable;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
@TableId(value = "id",type = IdType.ID_WORKER)
private Long id;
private String name;
private Integer age;
private String email;
// 和数据库字段绑定;填充:新增数据时填充
@TableField(value = "create_time",fill= FieldFill.INSERT)
private Date createTime;
// 和数据库字段绑定;填充:新增数据时必须填入,修改数据时也需要填入
@TableField(value = "update_time",fill= FieldFill.INSERT_UPDATE)
private Date updateTime;
}
编写处理器类,针对该注解信息自动处理数据:
package cn.linkpower.config;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import java.util.Date;
@Slf4j
@Component // 将组件加载至spring的ioc容器中
public class MyMetaObjectHandler implements MetaObjectHandler {
/**
* 插入时的填充策略
* @param metaObject
*/
@Override
public void insertFill(MetaObject metaObject) {
log.info("---- insertFill ---");
// 插入数据时
// 给 createTime 设定时间
this.setFieldValByName("createTime",new Date(),metaObject);
// 给 update_time 设定时间
this.setFieldValByName("updateTime",new Date(),metaObject);
}
/**
* 更新时的填充策略
* @param metaObject
*/
@Override
public void updateFill(MetaObject metaObject) {
log.info("---- updateFill ---");
// 更新操作时,只需要修改 update_time 的数据即可
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}
执行新增数据
操作:
@Test
public void insertTest(){
User user = new User();
//user.setId(66l); // type = IdType.INPUT
user.setName("香蕉 time test");
user.setAge(28);
user.setEmail("123456@");
int insert = userMapper.insert(user);
System.out.println("-->"+insert);
System.out.println("-->"+user);
}
此时数据库中的数据信息为:
执行一下修改操作:
@Test
public void testUpdate(){
User user = new User();
user.setId(1439160297374285825l); // id为1
user.setName("香蕉 时间测试 222");
user.setAge(55); // 原数据为 18
int updateNum = userMapper.updateById(user);
System.out.println("--->"+updateNum);
System.out.println("--->"+user);
}
乐观锁
在平时的学习和工作中,总会听见数据库的乐观锁
、悲观锁
等概念。在 Mybatis-Plus 中,也对其进行了技术支持。
详见官方文档:
https://mp.baomidou.com/guide/interceptor-optimistic-locker.html#optimisticlockerinnerinterceptor
【疑问:】乐观锁和悲观锁 到底是什么?
-
乐观锁:
顾名思义,十分乐观,认为不会出现问题;无论干什么,都不去进行加锁。
如果出现问题,就再更新值测试! -
悲观锁:
顾名思义,十分悲观,认为不论干什么总会出现问题;无论干什么,都会上锁!
本篇博客主要说明 乐观锁
机制。
在官方文档中,已经给定了测试方式。
当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
实现步骤
关于
乐观锁
的实现方式,mybatis-plus
已经为我们做好了实现方式,只需要按照其给定的方式编写代码和配置即可。
1、修改对应的数据库表,增加version
字段,并设定默认值为1
。
2、修改对应的pojo
中的User.java
类,增加对应的属性
。同时使用@com.baomidou.mybatisplus.annotation.Version
注解对其修饰。
package cn.linkpower.pojo;
import com.baomidou.mybatisplus.annotation.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
@TableId(value = "id",type = IdType.ID_WORKER)
private Long id;
private String name;
private Integer age;
private String email;
@Version // 使用mybatis-plus的注解信息,表明这是一个“乐观锁”
private Integer version;
// 和数据库字段绑定;填充:新增数据时填充
@TableField(value = "create_time",fill= FieldFill.INSERT)
private Date createTime;
// 和数据库字段绑定;填充:新增数据时必须填入,修改数据时也需要填入
@TableField(value = "update_time",fill= FieldFill.INSERT_UPDATE)
private Date updateTime;
}
【注意:】
这里需要注意一点:
- 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
- 整数类型下 newVersion = oldVersion + 1
- newVersion 会回写到 entity 中
- 仅支持 updateById(id) 与 update(entity, wrapper) 方法
- 在 update(entity, wrapper) 方法下, wrapper 不能复用!!!
3、使用 OptimisticLockerInnerInterceptor
组件,对其进行配置。
在官方文档中,提供了两种方式:旧版
和新版
。
如果是直接使用旧版配置,只需要配置下列bean即可:
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
如果使用新版,则需要先保证mybatis-plus
的版本为3.4.0
。
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.0</version>
</dependency>
其次使用下列配置项:
package cn.linkpower.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableTransactionManagement // 开启事务,默认是开启的
// @MapperScan("cn.linkpower.mapper") //通常会把mapper的扫描放在此处
public class MPOptimisticLockerConfig {
/**
* 旧版
*/
// @Bean
// public OptimisticLockerInterceptor optimisticLockerInterceptor() {
// return new OptimisticLockerInterceptor();
// }
/**
* 新版
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mybatisPlusInterceptor;
}
}
4、编写测试类进行测试。
这里的测试,分两种方式进行测试:单线程
和多线程
。
首先来看单线程环境下
,代码测试结果:
// 测试乐观锁(单线程环境下)
@Test
public void testOptimisticLocker1(){
// 1、查询数据库获取当前待操作数据的基本信息,主要是version
User user = userMapper.selectById(2L);
log.info("查询数据信息为:{}",user.toString());
// 2、更新数据信息
user.setName("香蕉1");
user.setAge(18);
// 3、执行更新操作
userMapper.updateById(user);
log.info("更新后的数据:{}",user.toString());
}
数据库中的原始数据:
执行后的数据信息:
【发现:】
能够修改乐观锁的版本 version 字段信息。
上面验证了正常逻辑,再看一个多线程环境下的异常逻辑。
@Test
public void testOptimisticLocker2(){
// 线程1 查询操作
User user1 = userMapper.selectById(2L);
user1.setName("香蕉1111");
user1.setAge(18);
// 在线程1之前修改操作之前,进行了线程2的插队操作
User user2 = userMapper.selectById(2L);
user2.setName("香蕉2222");
user2.setAge(18);
// 线程2优先进行了提交操作
int updateNum2 = userMapper.updateById(user2);
log.info("线程2修改数据条数:{}",String.valueOf(updateNum2));
// 线程1 的提交修改操作
int updateNum1 = userMapper.updateById(user1);// 如果乐观锁正常,此处一定是修改数据条数为0
log.info("线程1修改数据条数:{}",String.valueOf(updateNum1));
}
查看控制台日志如下所示:
查询 select
查询单个用户信息
@Test
public void testSelectOne(){
User user = userMapper.selectById(1L);
System.out.println(user);
}
查询多个用户信息
// 查询多个用户
@Test
public void testSelectMore(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3, 4));
users.forEach(System.out::println);
}
使用map封装条件,进行查询
上述的两种方式实现固有的主键进行查询,但现实开发中,单独的主键id查询很少,通常都会有很多附加条件。
使用Map针对条件进行保证,即可达到大部分的条件查询。
@Test
public void testSelectByMap(){
HashMap<String, Object> map = new HashMap<>();
map.put("name","香蕉 时间测试 222");
map.put("age",55);
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
【注意:】如果条件名称错误,运行会报错!
org.springframework.jdbc.BadSqlGrammarException:
分页查询
简单的查询操作,往往在项目中还不够,针对数据量多的表,一般还会有分页查询。
一般分页操作有如下几种实现方式:
- 原生sql使用limit进行分页操作
- 使用第三方插件(如:PageHelper)进行分页
- Mybatis-Plus自带分页插件
在 官方文档 中,针对分页插件
就有很详细的说明配置等。
使用Mybatis-Plus自带分页插件
需要先编写一个分页插件的配置类。
使用到的依赖版本为:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.0</version>
</dependency>
配置文件:
package cn.linkpower.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableTransactionManagement // 开启事务,默认是开启的
public class MPOptimisticLockerConfig {
/**
* 旧版
*/
// @Bean
// public OptimisticLockerInterceptor optimisticLockerInterceptor() {
// return new OptimisticLockerInterceptor();
// }
/**
* 新版
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
// 乐观锁
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
// 分页
// 官方给的案例是H2数据库,我们测试使用的是MySQL,此处需要做更改
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return mybatisPlusInterceptor;
}
// 分页旧版
// @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;
// }
}
配置类编写完成后,只需要使用其给的一个内置Page对象即可。
// 普通分页查询测试
@Test
public void testSelectPage(){
Page<User> page = new Page<>(1,3);
Page<User> queryPage = userMapper.selectPage(page, null);
page.getRecords().forEach(System.out::println);
log.info("========================");
queryPage.getRecords().forEach(System.out::println);
}
如果把Page
中的数据修改成:
Page<User> page = new Page<>(2,3);
此时的控制台sql如下:
数据的回写。
删除 delete
普通删除
删除操作在BaseMapper
中提供了好几种方式,接下来一起看看都有什么效果。
删除指定id的信息
数据库指定的表原数据信息如下所示:
// 删除指定id的数据信息
@Test
public void testDeleteOne(){
int delete = userMapper.deleteById(1439160297374285825L);
log.info("删除行数:{}",delete);
}
批量删除
// 删除指定id的数据信息
@Test
public void testDeleteMore(){
int deleteBatchIds = userMapper.deleteBatchIds(Arrays.asList(1439160297374285825L, 1439155369759154178L));
log.info("删除行数:{}",deleteBatchIds);
}
条件删除
// 删除指定id的数据信息
@Test
public void testDeleteByMap(){
HashMap<String, Object> map = new HashMap<>();
map.put("name","香蕉 时间测试 222");
map.put("age",55);
int deleteByMap = userMapper.deleteByMap(map);
log.info("删除行数:{}",deleteByMap);
}
普通删除就是使用条件
,直接删除指定数据库中的数据信息。但有些公司,需要保存一些基本的信息时,并不是能直接删除表中的数据的。这个时候就需要使用到另一个高大上的删除方式:逻辑删除
。
逻辑删除
逻辑删除到底是什么?
删除数据,数据并不会从数据库中直接移除,而是通过一个变量来让其失效!
接下来就来看怎么玩的。
配置类可以参考官方文档:
https://mp.baomidou.com/guide/logic-delete.html#%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95
下面是自己的配置顺序:
1、首先,在数据库对应表中增加一个deleted
字段,并设定初始值为0
。
2、pojo实体类中增加属性
package cn.linkpower.pojo;
import com.baomidou.mybatisplus.annotation.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
@TableId(value = "id",type = IdType.ID_WORKER)
private Long id;
private String name;
private Integer age;
private String email;
@Version // 乐观锁版本注解
private Integer version; // 乐观锁
@TableLogic // 逻辑删除注解
private Integer deleted; // 逻辑删除
// 和数据库字段绑定;填充:新增数据时填充
@TableField(value = "create_time",fill= FieldFill.INSERT)
private Date createTime;
// 和数据库字段绑定;填充:新增数据时必须填入,修改数据时也需要填入
@TableField(value = "update_time",fill= FieldFill.INSERT_UPDATE)
private Date updateTime;
}
3、增加配置类,对@TableLogic
的识别和监测
#逻辑删除配置
# 全局逻辑删除的实体字段名(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 testLogicDeleteOne(){
int delete = userMapper.deleteById(1L);
log.info("删除行数:{}",delete);
}
原始数据为:
执行删除方法后,数据库数据为:
控制台日志输出打印如下所示:
5、删除
操作执行完成后,再次查询这个被删
的记录呢?
@Test
public void testSelectOne(){
User user = userMapper.selectById(1L);
System.out.println(user);
}
【疑问:】逻辑删除了,该怎么查看被删除的数据呢?
自己写SQL!
mybatis-plus 只是提供了简单的CRUD
操作,还是可以自己写SQL的!
条件构造器
在上面的查询操作中,针对很多条件的查询,我们通常采取的是使用Map
集合封装查询条件和数据信息,实现基本的查询操作。
但是其执行后,SQL自动拼接成条件 = ?
的方式,如果需要使用== 、!= 、like
等这些,却显得力不从心。下面我们来一起看看什么是Wapper 条件构造器
。
https://mp.baomidou.com/guide/wrapper.html
eq、allEq、ne
命令 | 含义 | 栗子 |
---|---|---|
allEq | 全部匹配 | allEq({id:1,name:“老王”,age:null}) —> id = 1 and name = ‘老王’ and age is null |
eq | = 等于 | eq(“name”, “老王”)—>name = ‘老王’ |
ne | <> 不等于 | ne(“name”, “老王”)—>name <> ‘老王’ |
例如:数据库中的数据信息如下所示:
案例:查找id不为1
且age为18
的记录数。
@Test
public void test1(){
// 使用 Wapper 接口的子实现类,查询操作使用 QueryWrapper
QueryWrapper<User> wapper = new QueryWrapper<>();
wapper.ne("id",1).eq("age",18);
List<User> userList = userMapper.selectList(wapper);
userList.forEach(System.out::println);
}
大小区分
命令 | 含义 | 栗子 |
---|---|---|
gt | > | gt(“age”, 18)—>age > 18 |
ge | >= | ge(“age”, 18)—>age >= 18 |
lt | < | lt(“age”, 18)—>age < 18 |
le | <= | le(“age”, 18)—>age <= 18 |
案例:查找id大于等于1
且年龄小于等于30
的数据记录。
@Test
public void test2(){
// 使用 Wapper 接口的子实现类,查询操作使用 QueryWrapper
QueryWrapper<User> wapper = new QueryWrapper<>();
wapper.ge("id",1).le("age",30);
List<User> userList = userMapper.selectList(wapper);
userList.forEach(System.out::println);
}
范围查询
命令 | 含义 | 栗子 |
---|---|---|
between | BETWEEN 值1 AND 值2 | between(“age”, 18, 30)—>age between 18 and 30 |
notBetween | NOT BETWEEN 值1 AND 值2 | notBetween(“age”, 18, 30)—>age not between 18 and 30 |
案例:查询年龄在20到30岁之间的数据。
//查询年龄在20到30岁之间的数据。
@Test
public void test3(){
// 使用 Wapper 接口的子实现类,查询操作使用 QueryWrapper
QueryWrapper<User> wapper = new QueryWrapper<>();
wapper.between("age",20,30);
List<User> userList = userMapper.selectList(wapper);
userList.forEach(System.out::println);
}
模糊查询
命令 | 含义 | 栗子 |
---|---|---|
like | LIKE ‘%值%’ | like(“name”, “王”)—>name like ‘%王%’ |
notLike | NOT LIKE ‘%值%’ | notLike(“name”, “王”)—>name not like ‘%王%’ |
likeLeft | LIKE ‘%值’ | likeLeft(“name”, “王”)—>name like ‘%王’ |
likeRight | LIKE ‘值%’ | likeRight(“name”, “王”)—>name like ‘王%’ |
栗子:查询name 以香蕉开头、以2结尾
的记录。
@Test
public void test4(){
// 使用 Wapper 接口的子实现类,查询操作使用 QueryWrapper
QueryWrapper<User> wapper = new QueryWrapper<>();
wapper.likeRight("name","香蕉").likeLeft("name","2");
List<User> userList = userMapper.selectList(wapper);
userList.forEach(System.out::println);
}
isNull、isNotNull
命令 | 含义 | 栗子 |
---|---|---|
isNull | IS NULL | isNull(“name”)—>name is null |
isNotNull | IS NOT NULL | isNotNull(“name”)—>name is not null |
栗子:查找create_time为空
且update_time不为空的记录
。
@Test
public void test5(){
// 使用 Wapper 接口的子实现类,查询操作使用 QueryWrapper
QueryWrapper<User> wapper = new QueryWrapper<>();
wapper.isNull("create_time").isNotNull("update_time");
List<User> userList = userMapper.selectList(wapper);
userList.forEach(System.out::println);
}
in、notIn、inSql
命令 | 含义 | 栗子 |
---|---|---|
in | 列 in (xx,xx,xx,) | in(“age”, 1, 2, 3)—>age in (1,2,3) |
nitIn | 字段 NOT IN (xx,xx,xx,) | notIn(“age”,{1,2,3})—>age not in (1,2,3) |
inSql | 字段 IN ( sql语句 ) | inSql(“age”, “1,2,3,4,5,6”)—>age in (1,2,3,4,5,6) inSql(“id”, “select id from table where id < 3”)—>id in (select id from table where id < 3) |
栗子:查询email为 123456@
的所有用户信息。
本来可以直接采取
eq
拿取数据,此处只是为了做测试!
@Test
public void test6(){
// 使用 Wapper 接口的子实现类,查询操作使用 QueryWrapper
QueryWrapper<User> wapper = new QueryWrapper<>();
wapper.inSql("id","select id from user where email = '123456@'");
List<User> userList = userMapper.selectList(wapper);
userList.forEach(System.out::println);
}
排序
命令 | 含义 | 栗子 | 标注 |
---|---|---|---|
orderBy | ORDER BY 字段, … | orderBy(true, true, “id”, “name”)—>order by id ASC,name ASC | 默认升序 |
orderByAsc | ORDER BY 字段, … ASC | orderByAsc(“id”, “name”)—>order by id ASC,name ASC | 升序 |
orderByDesc | ORDER BY 字段, … DESC | orderByDesc(“id”, “name”)—>order by id DESC,name DESC | 降序 |
栗子:查询id 升序
且name降序
的记录。
@Test
public void test7(){
// 使用 Wapper 接口的子实现类,查询操作使用 QueryWrapper
QueryWrapper<User> wapper = new QueryWrapper<>();
wapper.orderByAsc("id").orderByDesc("name");
List<User> userList = userMapper.selectList(wapper);
userList.forEach(System.out::println);
}
性能分析插件
Mybatis-plus 为我们提供了一种性能分析插件
。可以用来查看SQL执行的具体信息等,方便开发者判断哪些SQL需要进行优化操作。
在配置文件中添加如下的Bean
,并指定试用环境。
保证测试环境下进行分析,生产环境下不分析。
3.2.0 版本之前
@Bean
@Profile({"dev","test"})// 设置 dev test 环境开启
public PerformanceInterceptor performanceInterceptor() {
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
performanceInterceptor.setFormat(true);//格式化语句
//performanceInterceptor.setMaxTime(5);//ms 执行时间超过多少ms会抛出异常
return performanceInterceptor;
}
再配置项目环境信息:
spring.profiles.active=dev
3.2.0 版本之后
上述的配置操作,在3.2.0
版本进行了废除
!此时需要使用到官方给定的另外一种实现方式:
https://mp.baomidou.com/guide/p6spy.html
添加对应的p6syp
的依赖:
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.8.2</version>
</dependency>
其次,修改数据库连接配置信息:
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:p6spy:mysql://192.168.99.100:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriver
主要是变更driver-class-name
的驱动配置和url
中jdbc: 后添加 p6spy:
。
新增spy.properties
文件,其中内容如下所示:
#3.2.1以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
#3.2.1以下使用或者不配置
#modulelist=com.p6spy.engine.logging.P6LogFactory,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
配置完成后,执行下列查询语句:
// 查询多个用户
@Test
public void testSelectMore(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3, 4));
users.forEach(System.out::println);
}
控制台输出日志信息如下所示:
参考资料:
MyBatisPlus高级功能 ——SQL性能分析打印插件
代码下载
https://gitee.com/xiangjiaobunana/springboot-mybatis-plus
最后
以上就是贪玩枕头为你收集整理的Mybatis——Mybatis-Plus 看这一篇就够了博客资料参考官方文档数据库创建和数据添加创建SpringBoot项目简单CRUD操作条件构造器性能分析插件代码下载的全部内容,希望文章能够帮你解决Mybatis——Mybatis-Plus 看这一篇就够了博客资料参考官方文档数据库创建和数据添加创建SpringBoot项目简单CRUD操作条件构造器性能分析插件代码下载所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复