浅谈:MyBatis-Plus的CRUD与乐观锁,分页插件,逻辑删除
什么是MyBatis-Plus
请点击上面官方文档查看
代码演示:代码注释为功能详细解释
数据库
创建maven项目,基于springboot,mvc请参考官网
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.fs</groupId>
<artifactId>MyBatisPlus-HelloWorld</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- MyBatisPlus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
<!-- 由于引入了MyBatisPlus ,就不需要引入这个了,因为MyBatis-puls底层就是对MyBatis封装 spring MyBatis-->
<!-- <dependency>-->
<!-- <groupId>org.mybatis.spring.boot</groupId>-->
<!-- <artifactId>mybatis-spring-boot-starter</artifactId>-->
<!-- <version>2.1.3</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.20</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.yml
spring:
datasource:
username: root
password: root
url: jdbc:mysql://192.168.93.132:3306/test
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource #自定义数据源
# 配置开发环境
profiles:
active: dev
# 配置日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#配置逻辑删除
global-config:
db-config:
logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
主启动 MybatispulsHelloWorld
package com.fs;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MybatispulsHelloWorld {
public static void main(String[] args) {
SpringApplication.run(MybatispulsHelloWorld.class,args);
}
}
MyBatis-Plus配置类
package com.fs.config;
import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration//配置类
//开启事务管理
@EnableTransactionManagement
//使用MyBatis-puls要使用@MapperScan扫描dao的包
@MapperScan("com.fs.dao")
public class MybatisPlusConfig {
//注册乐观锁插件
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
//配置分页插件
@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;
}
//逻辑删除 新版在yml配置了,这里不用配置
// @Bean
// public ISqlInjector sqlInjector(){
// return new DefaultSqlInjector();
// }
// /**
// * SQL执行效率插件 在3.2上已被移除
// *
// * @return
// */
// @Bean
// @Profile({"dev"}) // 指定环境为dev生效
// public PerformanceInterceptor performanceInterceptor() {
// PerformanceInterceptor interceptor = new PerformanceInterceptor();
// // sql美化打印
// interceptor.setFormat(true);
// // 设置SQL超时时间
// interceptor.setMaxTime(5000L);
// return interceptor;
// }
}
自动填充功能类 MyMetaObjectHandler
package com.fs.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.util.Date;
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
//插入的时候充填策略
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
this.setFieldValByName("creatTime",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);
}
}
pojo实体类User 属性对应数据库表的字段
package com.fs.pojo;
import com.baomidou.mybatisplus.annotation.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.apache.ibatis.annotations.Delete;
import java.util.Date;
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class User {
//对应数据库中的主键,通过雪花算法
@TableId(type = IdType.ID_WORKER)
private Long id;
private String name;
private Integer age;
private String email;
// //在insert的时候插入
// @TableField(fill = FieldFill.INSERT)
// private Date creatTime;
// @TableField(fill = FieldFill.INSERT_UPDATE)
// private Date updateTime;
// //版本
// @Version//代表这是一个乐观锁的注解
// @TableField(value = "`version`")//处理数据库关键字
// private Integer version;
// //逻辑删除
// @TableLogic//这个注解就代表这个表中的字段存在逻辑
// @TableField(value = "`delete`")//处理数据库关键字
// private Integer delete;
}
mapper或者Dao接口 UserDao
这个接口要继承 BaseMapper<T>接口
package com.fs.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.fs.pojo.User;
//使用MyBatis-puls,只需要在UserDao接口上实现BaseMapper<实体类>
//这样,所有的CRUD都已经写完了,不需要在配置一大堆文件
public interface UserDao extends BaseMapper<User> {
}
测试类
package com.fs;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fs.dao.UserDao;
import com.fs.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@SpringBootTest
public class MybatispulsHelloWorldTest {
//userDao继承了BaseMapper.所有的方法都来自父类
//我们也可以自己编写自己的扩展方法
@Autowired
private UserDao userDao;
//测试查询所有
@Test
public void testUser(){
//selectList的参数是一个Wrapper 条件构造器,不传就填null
//查询全部用户
List<User> users = userDao.selectList(null);
for (User user : users) {
System.out.println(user);
}
}
//测试添加
@Test
public void testUserInsert(){
//会自动根据你传递的对象的属性值不为null的来动态sql 就是xml 中的if
/*
private Long id;
private String name;
private Integer age;
private String email;
*/
//添加一个用户
// userDao.insert(new User(null,"小付",18,"934226217@qq.com"));
User user = new User();
user.setName("测试2");
// user.setAge(12);
userDao.insert(user);
}
//测试更新
@Test
public void testUserupdateById(){
/*
private Long id;
private String name;
private Integer age;
private String email;
*/
User user = new User();
user.setName("小强");
user.setId(1293148203881086977L);
//添加一个用户
userDao.updateById(user);
}
//测试乐观锁成功
@Test
public void optimisticLocker(){
User user = userDao.selectById(1L);
user.setName("大大");
user.setAge(1);
user.setEmail("1213@qq.com");
userDao.updateById(user);
}
//测试乐观失败,多线程下
@Test
public void optimisticLocker02(){
//线程一
User user = userDao.selectById(1L);
user.setName("大大111");
user.setAge(1);
user.setEmail("1213@qq.com");
//模拟另一个线程执行插队抄作
User user1 = userDao.selectById(1L);
user1.setName("大大22");
user1.setAge(1);
user1.setEmail("1213@qq.com");
//插队,如果没有没有乐观锁就会被下面的覆盖
userDao.updateById(user1);
//由于上面更新操作后version+1,这里还是version.所以对比version值不一致,就不会对表中数据进行更新,这就是乐观锁
userDao.updateById(user);
}
//测试查询
@Test
public void selectUser(){
//selectList的参数是一个Wrapper 条件构造器,不传就填null
//查询全部用户
List<User> users = userDao.selectList(null);
for (User user : users) {
System.out.println(user);
}
}
//根据ids查询
@Test
public void selectUserByIds(){
//selectList的参数是一个Wrapper 条件构造器,不传就填null
//查询ids用户
List<User> users = userDao.selectBatchIds(Arrays.asList(1, 2, 3));
for (User user : users) {
System.out.println(user);
}
}
//条件查询使用map操作
@Test
public void selectUserByMap(){
HashMap<String, Object> map = new HashMap<>();
map.put("name","小强");
userDao.selectByMap(map).forEach(System.out::println);
}
//分页查询
//配置拦截器
@Test
public void findPage(){
//分页查询需要这个类,我们就new出来,传递当前页与每页条数
Page<User> userPage = new Page<>(2,5);
//Wrapper 没有就给null
Page<User> users = userDao.selectPage(userPage, null);
List<User> records = users.getRecords();
records.forEach(System.out::println);
long total = users.getTotal();
System.out.println("总数:"+total);
}
//删除
@Test
public void deleteUser(){
int i = userDao.deleteById(1);
System.out.println(i);
// userDao.deleteBatchIds(Arrays.asList(2,3,4));//批量id删除
// userDao.deleteByMap(new HashMap<>());//根据条件删除
}
//逻辑删除
@Test
public void deleteUser02(){
/*
物理删除:从数据库中直接删除,持久化
逻辑删除:在数据库中没有被移除,而是通过一个变量来让他失效!delete = 1
//使用场景:管理员查询被删除的记录!,防止数据的丢失,类似于回收站
//在数据表中增加delete字段
*/
//配置逻辑删除后,执行的sql语句是更新操作,将delete的字段值为1,就实现了逻辑删除,查询的时候会自动拼接delete = 0
//查询语句自动添加delete SELECT id,name,age,email,creat_time,update_time,`version`,`delete` FROM user WHERE `delete`=0
userDao.deleteById(2);
}
}
以查询所有为列子
说明我们不需要写任何sql.crud的sql,MyBatis-puls都帮我们自动生成,而且执行.