一、MyBatisPlus概述
MyBatisPlus(简称 MP)是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
MyBatis-Plus可以节省我们大量工作时间,所有的CRUD代码它都可以自动化完成!
同类技术框架有:JPA、tk-mapper、MyBatis-Plus。
MyBatisPlus官网地址:https://mybatis.plus/、https://baomidou.com/
MyBatisPlus项目地址:https://gitee.com/baomidou/mybatis-plus
分布式系统唯一ID生成方案汇总 - nick hao - 博客园 (cnblogs.com)
MyBatisPlus特性:
- 无侵入:只做增强不做改变,不会对现有工程产生影响。
- 损耗小:启动即会自动注入基本 CRUD(增删改查),性能基本无损耗,直接面向对象操作。
- 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表CRUD 操作,更有强大的条件构造器(QueryWrapper),满足各类使用需求。
- 支持 Lambda:编写查询条件无需担心字段写错。
- 支持主键自动生成:支持多达4种主键策略(内含分布式唯一ID生成器:Sequence)可自由配置,完美解决主键问题。
- 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作。
- 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )。
- 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用。
- 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询。
- 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库。
- 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询。
- 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作。
MyBatisPlus支持的数据库:(任何能使用MyBatis
进行CRUD操作,并且支持标准 SQL 的数据库)
-
mysql 、mariadb 、oracle 、db2 、h2 、hsql 、sqlite 、postgresql 、sqlserver 、presto 、Gauss 、Firebird。
-
Phoenix 、clickhouse 、Sybase ASE 、 OceanBase 、达梦数据库 、虚谷数据库 、人大金仓数据库 、南大通用数据库。
MyBatisPlus框架结构:
二、MyBatisPlus快速入门
文档地址:https://mybatis.plus/guide/quick-start.html
使用第三方组件的流程:
1、导入对应的依赖。
2、研究依赖如何配置。
3、代码如何编写。
4、提高扩展技术能力!
SpringBoot整合MP操作步骤如下:
1、准备数据:
create database if not exists mp_db character set utf8;
use mp_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(null,'tom','123456',12,'12345678910');
insert into user values(null,'jack','123456',8,'12345678910');
insert into user values(null,'jerry','123456',15,'12345678910');
insert into user values(null,'tom','123456',9,'12345678910');
insert into user values(null,'snake','123456',28,'12345678910');
insert into user values(null,'张益达','123456',22,'12345678910');
insert into user values(null,'张大炮','123456',16,'12345678910');
2、创建一个maven工程。
3、导入依赖:
<?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.baidou</groupId>
<artifactId>mp01_quickstart</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!--父工程-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- lombok,通过注解简化实体类的开发 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- mybatisplus的起步依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<!-- Junit起步依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
</project>
4、编写实体类:(类名与表名对应,属性名与字段名对应)(ORM对象关系映射)
package com.baidou.entity;
import lombok.Data;
//实体类
@Data
public class User {
private Long id;
private String name;
private String password;
private Integer age;
private String tel;
}
5、在application.yml
文件中配置jdbc相关参数:
# 配置数据源
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/mp_db?useSSL=false
username: root
password: 123456
# 配置日志级别为:debug (输出大于等于debug级别的日志信息)
#logging:
# level:
# com.baidou: debug
# mp配置
mybatis-plus:
configuration:
# 配置标准sql输出
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
6、定义mapper接口,继承BaseMapper。
package com.baidou.mapper;
import com.baidou.entity.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
//在对应的Mapper上面继承BaseMapper接口
//@Repository //代表持久层
@Mapper
public interface UserMapper extends BaseMapper<User> {
//所有的CRUD操作都已经编写完成了
//你不需要像以前配置一大堆文件了!
}
7、定义启动类,并配置mapper扫描:
package com.baidou;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.baidou.mapper") //扫描我们的mapper包下的所有接口
public class MyBatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(MyBatisPlusApplication.class, args);
}
}
8、编写测试
package com.baidou.test;
import com.baidou.entity.User;
import com.baidou.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
/**
* 测试类
*
* @author 白豆五
* @version 2023/1/23
* @since JDK8
*/
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
public void testFindAll() {
List<User> userList = userMapper.selectList(null); //null表示不传条件参数Wrapper
for (User user : userList) {
System.out.println(user);
}
}
}
运行结果:
SQL谁帮我们写的? MyBatisPlus都写好了。
方法哪里来的? MyBatisPlus都写好了。
三、标准数据层开发
mapper接口
1. MyBatisPlus的CRUD操作
功能 | 之前自定义的接口 | MP提供的接口 |
---|---|---|
新增 | boolean save(T entity) | int insert(T entity) |
删除 | boolean delete(int id) | int deleteById(Serializable id) |
修改 | boolean update(T entity) | int updateById(T entity) |
根据id查询 | T getById(int id) | T selectById(Serializable id) |
查询全部 | List getAll() | List selectList() |
分页查询 | PageInfo getAll(int age,int size) | IPage selectPage(IPage page) |
按条件查询 | List getAll(Condition condition) | IPage selectPage(Wrapper queryWrapper) |
示例代码:
package com.baidou.test;
import com.baidou.entity.User;
import com.baidou.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
/**
* 测试mapper接口的crud操作
*
* @author 白豆五
* @version 2023/1/23
* @since JDK8
*/
@SpringBootTest
public class UserMapperTest {
@Autowired
private UserMapper userMapper;
// 查询全部
@Test
public void testFindAll() {
List<User> userList = userMapper.selectList(null); //null表示不传条件参数
for (User user : userList) {
System.out.println(user);
}
}
// 新增,会自动返回新增记录的主键
@Test
public void testSave() {
User user = new User();
user.setName("小舞");
user.setAge(18);
user.setPassword("123456");
user.setTel("13212341234");
int num = userMapper.insert(user);
if (num==1){
System.out.println("添加成功");
//自动返回新增记录的主键
Long id = user.getId();
System.out.println("用户的id:"+id);//1617500546355167234
}
}
// 修改
@Test
public void testUpdate() {
User user = new User();
user.setId(5L);
user.setName("小米");
userMapper.updateById(user);
}
// 删除
@Test
public void testDelete() {
userMapper.deleteById(1527307439025266690L);
}
}
2. MyBatisPlus分页功能
MyBatisPlus内置分页插件。
2.1 MyBatisPlus分页功能接口
// 分页查询
IPage<T> selectPage(IPage<T> page)
2.2 MyBatisPlus分页使用
1、配置分页拦截器组件
package com.baidou.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
*MybatisPlus的配置类
* @author 白豆五
* @version 2023/1/23
* @since JDK8
*/
@Configuration
public class MybatisPlusConfig {
// 注册分页插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
//1 创建MP拦截器对象
MybatisPlusInterceptor mpInterceptor=new MybatisPlusInterceptor();
//2 添加分页拦截器
mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
//注意:不同的数据库在开启分页功能的时候,需要设置成对应的数据库类型 (数据库方言不太一样)
//mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return mpInterceptor;
}
}
2、开启MyBatisPlus日志
# 开启mp的sql日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
3、编写测试
// 分页查询
@Test
public void testSelectPage() {
// 1.创建IPage分页对象,设置分页参数
// 参数一:当前页
// 参数二: 页面大小
IPage<User> page = new Page<>(1, 3);//import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
// 2.执行分页查询
userMapper.selectPage(page, null);
// 3.获取分页结果
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、取消初始化spring日志打印
解决方案:在resources下新建一个logback.xml文件,名称固定,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
</configuration>
- logback常用配置:https://www.jianshu.com/p/75f9d11ae011
2、关闭springboot启动的beanner图标
spring:
main:
banner-mode: off # 关闭SpringBoot启动图标(banner)
3、关闭MybatisPlus启动的beanner图标
mybatis-plus:
global-config:
banner: off # 关闭mybatisplus启动图标
四、DQL编程控制
DQL数据查询语言,查询操作的。
1. 条件查询方式
MyBatisPlus将书写复杂的SQL查询条件进行了封装,使用编程的形式完成查询条件的组合。
Wrapper条件构造器(抽象父类):
- 子类:QueryWrapper (封装查询条件)、UpdateWrapper (封装修改条件)
- 子类:LambdaUpdateWrapper、LambdaQueryWrapper(lambda格式查询条件)
1.1 条件查询
方式1:条件查询(QueryWrapper)
@SpringBootTest
public class UserMapperTest {
ZSXT6
@Autowired
private UserMapper userMapper;
/**
* 按条件查询
*/
@Test
public void testQueryWrapper() {
// 1、创建条件构造器对象
QueryWrapper<User> qw = new QueryWrapper<>();
// 2、封装条件:年龄小于18
// lt小于, gt大于 (带t的)
// eq 等于, ge 大于等于 ,le小于等于,ne不等于 (带e的)
qw.lt("age", 18);
// 3、mp按照指定条件进行查询所有操作
List<User> userList = userMapper.selectList(qw);
System.out.println(userList);
}
}
方式2:条件查询——使用lambda表达式(QueryWrapper)
@Test
public void testQueryWrapperByLambda() {
QueryWrapper<User> qw = new QueryWrapper<User>();
qw.lambda().lt(User::getAge, 10); //LambdaQueryWrapper.lt(User::getAge, 10);
List<User> userList = userMapper.selectList(qw);
System.out.println(userList);
}
方式3:lambda格式条件查询(LambdaQueryWrapper)
@Test
public void testLambdaQueryWrapper() {
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
lqw.lt(User::getAge, 10);
List<User> userList = userMapper.selectList(lqw);
System.out.println(userList);
}
1.2 组合条件
并且关系(and)
// and : (10,25)
@Test
public void test1() {
LambdaQueryWrapper<User> qw = new LambdaQueryWrapper<>();
qw.lt(User::getAge,25).gt(User::getAge,10); // where age< 25 and age >10
List<User> users = userMapper.selectList(qw);
System.out.println(users);
}
// and : (10,25)
@Test
public void test2() {
LambdaQueryWrapper<User> qw = new LambdaQueryWrapper<>();
qw.lt(User::getAge,25).gt(User::getAge,10); // where age< 25 and age >10
List<User> users = userMapper.selectList(qw);
System.out.println(users);
}
注意:组合查询默认使用and。
或者关系(or)
@Test
public void test() {
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//或者关系:小于10岁或者大于30岁
lqw.lt(User::getAge, 10).or().gt(User::getAge, 30);//where age<10 or age>30
List<User> userList = userMapper.selectList(lqw);
System.out.println(userList);
}
1.3 NULL值处理
NULL值处理:先对条件值进行非空判断,然后再追加处理。
① if语句控制条件追加
先使用if非空校验然后再追加条件
@Test
public void test1() {
Integer minAge=10; //将来有用户传递进来,此处简化成直接定义变量了
//包装类型数据 不传值默认为null
Integer maxAge=null; //将来有用户传递进来,此处简化成直接定义变量了
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
if(minAge!=null){
lqw.gt(User::getAge, minAge);
}
if(maxAge!=null){
lqw.lt(User::getAge, maxAge);
}
List<User> userList = userMapper.selectList(lqw);
userList.forEach(System.out::println);
}
② 条件参数控制
简化第一种方式的if判断
@Test
public void test2() {
Integer minAge = 20;
Integer maxAge = null;
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<User>();
//参数1:如果表达式为true,那么查询才使用该条件
// (boolean condition, R column, Object val)
lqw.gt(minAge != null, User::getAge, minAge);
lqw.lt(maxAge != null, User::getAge, maxAge);
userMapper.selectList(lqw);
}
③ 条件参数控制(链式编程)
@Test
public void test3() {
Integer minAge = null;
Integer maxAge = 18;
LambdaQueryWrapper<User> qw = new LambdaQueryWrapper<>();
qw.gt(minAge != null, User::getAge, minAge);
qw.or();
qw.lt(maxAge != null, User::getAge, maxAge);
userMapper.selectList(qw);
}
2. 投影查询
投影查询:(设置查询字段、)
2.1 查询结果包含模型类中部分属性(设置查询字段)
@Test
public void test1() {
// 投影查询,设置查询字段
QueryWrapper<User> qw = new QueryWrapper<>();
qw.select("id","name","age","tel"); //设置查询字段
List<User> users = userMapper.selectList(qw);
System.out.println(users);
}
2.2 查询结果包含实体类中未定义的属性(分组)
@Test
public void test2() {
// 投影查询,分组查询
QueryWrapper<User> qw = new QueryWrapper<>();
// select xxxx from 表名
qw.select("name,count(*)"); //sql: select name,count(*) from user group by name;
qw.groupBy("name");
List<Map<String, Object>> maps = userMapper.selectMaps(qw);
System.out.println(maps);
}
3. 查询条件设定
多条件查询有哪些组合?
- 范围匹配(>大于gt 、 >=大于等于ge、<小于lt、<=小于等于le、!=不等于ne 、between)
- 模糊匹配(like)
- 非空判断(null)
- 包含性匹配(in)
- 排序(order)
……
3.1 范围查询
范围查询:大于gt 、 大于等于ge、小于lt、相等eq、不相等ne 、小于等于le、between。
1、eq的使用
// eq
@Test
public void testEQ() {
// 模拟用户登录
String name="小米";
String password="1234568";
LambdaQueryWrapper<User> qw = new LambdaQueryWrapper<>();
qw.eq(User::getName,name).eq(User::getPassword,password);
User user = userMapper.selectOne(qw);
System.out.println(user);
}
2、其他范围查询
//范围查询 ge(great then or equal) le(less then or equal) gt lt between
@Test
public void testRange() {
/*
(15,25) qw.gt(User::getAge,15).lt(User::getAge,25);
[15,25) qw.ge(User::getAge,15).lt(User::getAge,25);
(15,25] qw.gt(User::getAge,15).le(User::getAge,25);
[15,25] qw.ge(User::getAge,15).le(User::getAge,25);
*/
//[15,25]
LambdaQueryWrapper<User> qw = new LambdaQueryWrapper<>();
//qw.ge(User::getAge,15).le(User::getAge,25); // age>=15 and age <25
qw.between(User::getAge, 15, 25); //使用between简化上面的写法
List<User> users = userMapper.selectList(qw);
System.out.println(users);
}
3.2 模糊匹配
模糊查询:like、likeLeft、likeRight
@Test
public void testLike() {
QueryWrapper<User> qw = new QueryWrapper<>();
// 匹配中间内容是xxx的
// qw.like("name","a"); // select * from user where name like %a%
// 匹配以xxx结尾
qw.likeLeft("name","炮"); // select * from user where name like %炮
// 匹配以xxx开头
qw.likeRight("name","小"); // select * from user where name like 小%
List<User> users = userMapper.selectList(qw);
System.out.println(users);
}
3.3 排序查询
排序:降序orderByDesc、升序orderByAsc。
@Test
public void testOrder(){
QueryWrapper<User> qw = new QueryWrapper<>();
qw.orderByDesc("age"); // select * from user order by age desc
List<User> users = userMapper.selectList(qw);
System.out.println(users);
}
3.4 包含性匹配
包含: in
// 包含:in
@Test
public void testIn(){
QueryWrapper<User> qw = new QueryWrapper<>();
qw.in("age",16,18,20);
List<User> users = userMapper.selectList(qw);
System.out.println(users);
}
巴拉巴拉
4. 字段映射与表名映射
4.1 解决表字段与实体类中属性名称不同步问题
解决方案:在实体类属性上,使用属性注解@TableField,通过value属性,设置当前属性对应的数据库表中的字段关系。
4.2 问题二:实体类中添加了数据库中未定义的属性
解决方案:在实体类属性上使用@TableField注解,通过exist属性,设置属性在数据库表字段中是否存在,默认为true。此属性无法与value合并使用。
4.3 解决采用默认查询会开放更多的字段查看权限
例如我们不想把查询的密码直接暴露给接口。
解决方案:在实体类属性上使用@TableField注解,通过select属性:设置该属性是否参与查询。此属性与value映射配置不冲突。
4.4 解决表名与实体类名不同步问题
解决方案:在实体类上使用@TableName注解,通过value属性,设置当前类对应的数据库表名称。
扩展:
如果有许多实体都需要这样配置表名(例如开头是tb_xxx或tbl_xxx),可以在mp的全局配置中设置表的前缀。
mybatis-plus:
global-config: #全局配置
db-config:
table-prefix: tb_ #设置表的前缀
这样实体类就不用再配置@TableName了。
package com.baidou.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
@Data
public class User {
private Long id;
private String name;
@TableField(value = "password",select = false)
private String pwd;
private Integer age;
private String tel;
}
五、DML编程控制
DML:数据操纵语言
1. 主键生成策略(Insert)
主键生成的策略有哪几种方式?(根据不同场景应用的id生成策略也不相同)
- 日志:自增(1,2,3,4,……)
- 购物订单:特殊规则(FQ23948AK3843)
- 外卖单:关联地区日期等信息(10 04 20200314 34 91)
- 关系表:可省略id
1.1 主键生成策略控制(@TableId注解)
在实体类的主键属性上,使用属性注解@TableId,通过type属性设置主键生成策略。
type属性值参考IdType枚举值:(5种常用的生成策略,@Deprecated注解表示过时了)
AUTO
:使用数据库的自增策略(mysql:auto_increment)控制id生成。NONE
:不设置id生成策略。(如果插入数据设置了id,那么它就用你设置的id,如果未指定id它默认用雪花算法生成id)INPUT
:用户手工输入id。ASSIGN_ID
:雪花算法生成id(可兼容数值型与字符串型)。ASSIGN_UUID
:以UUID生成算法作为id生成策略。(字符串)
示例:
@Data
public class User {
//@TableId(type = IdType.AUTO)
@TableId(type = IdType.ASSIGN_ID)
private Long id;
private String name;
@TableField(value = "password",select = false)
private String pwd;
private Integer age;
private String tel;
}
雪花算法
雪花算法(snowflake)是推特开源的分布式ID生成算法,结果是一个long型的ID(一共64位)。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。
1.2 全局策略配置
如果某个类的主键不想使用全局配置的雪花算法,只需在该主键上配置@TableId的生成策略即可。
mybatis-plus:
global-config:
db-config:
#id生成策略全局配置
id-type: assign_id
# 表名前缀全局配置
table-prefix: tb_
2. 批量操作(select、delete操作)
批量查询selectBatchIds、批量删除deleteBatchIds
2.1 按照主键批量删除多条记录
//用list集合存储要删除的主键id
List<Long> list = new ArrayList<>();
list.add(1402551342481838081L);
list.add(1402553134049501186L);
list.add(1402553619611430913L);
//批量删除
userMapper.deleteBatchIds(list);
2.2 根据主键批量查询多条记录
//批量查询
userMapper.selectBatchIds(Arrays.asList(1L,3L,4L));
3. 逻辑删除(Delect、Update操作)
在实际环境中,如果想删除一条数据,是否会真的从数据库中删除该条数据?
-
物理删除 : 将数据直接从数据库中干掉。
-
逻辑删除:为数据设置是否可用状态字段,删除时设置状态字段为不可用状态,但数据仍保留在数据库中。
逻辑删除快速入门
1、为数据库表添加逻辑删除字段。(设置默认值0,长度1,int类型)
2、在实体类中添加对应字段,并用@TableLogic注解配置当前字段为逻辑删除标记字,。
@Data
public class User {
private Long id;
private String name;
private String password;
private Integer age;
private String tel;
//逻辑删除字段,标记当前记录是否被删除 1:删除 0:未删
@TableLogic(value = "0",delval = "1")
private Integer isDeleted;
}
3、开启mp驼峰映射(mp默认字段开启)
mybatis-plus:
configuration:
# 开启驼峰映射
map-underscore-to-case: true
4、执行删除操作
@SpringBootTest
public class UserMapperLogicDeleteTest {
@Autowired
private UserMapper userMapper;
@Test //junit5
public void testLogicDelete() {
int count = userMapper.deleteById(1619522751700262920L);
if (count>0){
System.out.println("删除成功");
}
}
}
执行逻辑删除前的表数据:
执行逻辑删除后的表数据:
注意:逻辑删除的本质其实是修改操作。如果加了逻辑删除字段,查询数据时也会自动带上逻辑删除字段。
扩展:逻辑删除全局配置
1、注掉实体类中逻辑删除注解
2、在yml中配置逻辑删除
mybatis-plus:
global-config:
db-config:
table-prefix: tbl_
# 逻辑删除字段名
logic-delete-field: deleted
# 逻辑删除字面值:未删除为0
logic-not-delete-value: 0
# 逻辑删除字面值:删除为1
logic-delete-value: 1
3、测试
执行逻辑删除前的表数据:
执行逻辑删除后的表数据:
4. 乐观锁(Update操作)
乐观锁、悲观锁详解:https://blog.csdn.net/qq_34337272/article/details/81072874
1、在user表中添加一个锁标记字段:
2、实体类中添加对应属性,并配置当前属性为乐观锁标记字段
@Data
public class User {
private Long id;
private String name;
private String password;
private Integer age;
private String tel;
@Version
private Integer version;
private Integer isDeleted;
}
3、配置乐观锁拦截器,实现锁机制对应的动态SQL语句拼装
package com.baidou.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MpConfig {
@Bean
public MybatisPlusInterceptor mpInterceptor() {
//定义Mp拦截器
MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();
//分页插件
//...
//乐观锁插件
mpInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mpInterceptor;
}
}
4、测试,使用乐观锁机制在修改前必须先获取到对应数据的verion才能正常进行
@Test
public void testUpdate() {
// 通常在开发中先查询(有版本),后更新
User user1 = userMapper.selectById(1L);
User user2 = userMapper.selectById(1L);
user1.setName("SpringCloud");
user2.setName("Nacos");
// 模拟并发修改
userMapper.updateById(user1);
userMapper.updateById(user2);
}
5. 字段自动填充
在开发中有些值是固定的(比如 创建时间、修改时间、创建人、修改人巴拉巴拉),而每次执行相关操作时都需要手动set,而mp提供了自动填充功能,不用手动封装那些数据了。
字段填充快速入门:
1、给user表添加两个字段create_time、update_time:
2、在实体类中需要填充的字段上通过@TableField注解的fill属性设置填充的时机
@Data
public class User {
private Long id;
private String name;
private String password;
private Integer age;
private String tel;
@Version
private Integer version;
@TableField(fill = FieldFill.INSERT) //新增的时候进行字段填充
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE) //新增或修改的时候进行字段填充
private LocalDateTime updateTime;
private Integer isDeleted;
}
3、编写一个配置类
实现MetaObjectHandler接口
package com.baidou.config;
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;
// 重新MetaObjectHandler接口的字段自动填充时机方法
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
// 新增的自动填充
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
//方式1
//this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
//this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
//方式2
//setFieldValByName("createTime",LocalDateTime.now(),metaObject);
//setFieldValByName("updateTIme",LocalDateTime.now(),metaObject);
//方式3
metaObject.setValue("createTime", LocalDateTime.now());
metaObject.setValue("updateTime",LocalDateTime.now());
}
// 修改的自动填充
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
}
4、测试
① 测试新增:
@Test
public void testInsert() {
User user = new User();
user.setName("王强");
user.setAge(23);
user.setPassword("233333");
user.setTel("13121341234");
int num = userMapper.insert(user);
System.out.println("行数:" + num);
System.out.println("id:" + user.getId());//id:1643892200968327170 主键自动返回
}
② 测试修改:
@Test //动态sql
public void testUpdate() {
User user = new User();
user.setId(1643892200968327170L);
user.setName("eureka");
int num = userMapper.updateById(user);
}
六、代码生成器
使用代码生成器好处:通过表,快速生成对应的实体类、mapper、service、controller代码(会有点小坑)
1、创建一个springboot基础工程
2、导入mp代码生成器相关依赖:
<!--代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<!--velocity模板引擎-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
3、编写代码生成器类
package com.baidou.util;
import com.baomidou.mybatisplus.annotation.IdType;
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;
/**
* 代码生成器
*/
public class CodeGenerator {
public static void main(String[] args) {
//1.获取代码生成器的对象
AutoGenerator autoGenerator = new AutoGenerator();
//设置数据库相关配置
DataSourceConfig dataSource = new DataSourceConfig();
dataSource.setDriverName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/mp_db?useSSL=false&serverTimezone=UTC");
dataSource.setUsername("root");
dataSource.setPassword("123456");
autoGenerator.setDataSource(dataSource);
//设置全局配置
GlobalConfig globalConfig = new GlobalConfig();
String projectName = "mp04_test"; //工程名
globalConfig.setOutputDir(System.getProperty("user.dir") + "/" + projectName + "/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.baidou"); //设置生成的包名,与代码所在位置不冲突,二者叠加组成完整路径
//packageInfo.setEntity("pojo"); //设置实体类包名 默认是entity
//packageInfo.setMapper("dao"); //设置数据层包名 默认是mapper
autoGenerator.setPackageInfo(packageInfo);
//策略设置
StrategyConfig strategyConfig = new StrategyConfig();
//strategyConfig.setInclude("tbl_user"); //设置对指定表进行代码生成,参数为可变参数,不设置的话就是全部表
strategyConfig.setTablePrefix("tbl_"); //设置隐藏数据库表的前缀名称,模块名 = 数据库表名 - 前缀名 例如: User = tbl_user - tbl_
strategyConfig.setRestControllerStyle(true); //设置Controller是否启用Rest风格
strategyConfig.setVersionFieldName("version"); //设置乐观锁字段名
strategyConfig.setLogicDeleteFieldName("deleted"); //设置逻辑删除字段名
strategyConfig.setEntityLombokModel(true); //设置是否启用lombok
autoGenerator.setStrategy(strategyConfig);
//2.执行生成操作
autoGenerator.execute();
}
}
效果如下:
七、条件构造器Wrappers
通用格式:
Wrappers.<实体类>lambdaQuery().xxx
Wrappers.<实体类>lambdaUpdate().xxx
测试:
@Test
public void testWarppers() {
// LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
// wrapper.like(User::getName,"小");
// List<User> users = userMapper.selectList(wrapper);
// 使用wrapper简化上面的lambda写法
List<User> userList = userMapper.selectList(Wrappers.<User>lambdaQuery().like(User::getName, "小"));
userList.forEach(System.out::println);
}