目录
1,MyBatis Plus 概述
1.1:简介
1.2:特色
2,入门案例
2.1:搭建环境
2.2:数据库和表
2.3:入门:查询所有
3,基本操作
3.1:常见API
3.2:添加
3.3:更新
3.4:删除
4,查询
4.1:Map条件
4.2:Wrapper条件
4.2.1:wrapper介绍
4.2.2:条件查询
4.3.3:条件更新
4.3:分页
4.3.1:内置插件
4.3.2:配置类
4.4.3:分页
5,常见注解
5.1:表名注解:@TableName
5.2:主键注解:@TableId
5.3:字段注解(非主键):@TableField
6,常见配置
7,高级(插件机制)
7.1:自动填充
7.1.1:原理
7.1.2:基本操作
7.2:乐观锁
7.2.1:什么是乐观锁
7.2.2:实现
7.2.3:注意事项
7.3:逻辑删除
7.3.1:内置插件
7.3.2:实现
7.3.3:注意
7.3.4:全局配置
7.3.5:恢复
8,通用service
8.1:分析
8.2:基本使用
8.3:常见方法
9,新功能
9.1:执行SQL分析打印
9.2:数据安全保护
- 个人主页: 爱吃豆的土豆
- 版权: 本文由【爱吃豆的土豆】原创、在CSDN首发、需要转载请联系博主
- 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦
人必有所执,方能有所成!
希望大家多多支持一起进步呀!
在宏观上我们能用它来什么
将Mybatis + 通用Mapper + PageHelper,把它们打包
升级成 MyBatis Plus
,
简单来说:它们三个能做的 MyBatis Plus能做,它们三个不能做的
MyBatis Plus也能做
官网:https://baomidou.com/
参考教程:简介 | MyBatis-Plus
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
无侵入:只做增强不做改变,引入它不会对现在的程序造成影响。
依赖少:仅仅依赖 Mybatis 以及 Mybatis-Spring 。
损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
多种主键策略:支持多达4种主键策略(内含分布式唯一ID生成器),可自由配置,完美解决主键问题 。
支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错(方法中的条件可以Lambda进行编写)
支持多种数据库:因为它是操作数据库所以支持很多数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer2005、SQLServer 等多种数据库
支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
支持 XML方式:Mapper 对应的 XML 支持热加载,对于简单的 CRUD 操作,甚至可以无 XML 启动,
支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
支持关键词自动转义:支持数据库关键词(order、key......)自动转义,还可自定义关键词,就是自动给关键字加上了重音符,避免了因为关键字导致sql语句不能执行的这种问题
支持代码生成:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用,学习过程中不推荐使用
内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
内置 Sql 注入剥离器:支持 Sql 注入剥离,有效预防 Sql 注入攻击
例如:
select * from user where username = 'jack' and password = '1234';
-- 将 jack 替换成 jack' #
-- sql注入,用户输入的内容,称为sql语句语法的一部分。
select * from user where username = 'jack' #' and password = '1234';
步骤一:创建项目:test-mybatis-plus
步骤二:修改pom.xml,添加依赖
org.springframework.boot
spring-boot-starter-parent
2.3.5.RELEASE
org.springframework.boot
spring-boot-starter-web
mysql
mysql-connector-java
org.projectlombok
lombok
org.springframework.boot
spring-boot-starter-test
com.baomidou
mybatis-plus-boot-starter
3.4.0
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/cloud_db1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username: root
password: 1234
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #输出日志 就是可以在控制台上看到执行中的sql语句
CREATE DATABASE cloud_db1;
USE cloud_db1;
CREATE TABLE `tmp_customer` (
`cid` INT(11) NOT NULL AUTO_INCREMENT,
`cname` VARCHAR(50) DEFAULT NULL,
`password` VARCHAR(32) DEFAULT NULL,
`telephone` VARCHAR(11) DEFAULT NULL,
`money` DOUBLE DEFAULT NULL,
`version` INT(11) DEFAULT NULL,
`create_time` DATETIME DEFAULT NULL,
`update_time` DATETIME DEFAULT NULL,
PRIMARY KEY (`cid`)
);
步骤
步骤1:配置JavaBean,添加MyBatisPlus对应的注解(表、主键、字段等)
步骤2:编写dao接口,并继承BaseMapper接口
步骤3:编写启动类
步骤4:编写测试类
步骤1:配置JavaBean
@TableName
表名注解,value属性设置表名
package com.czxy.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.util.List;
@Data
@TableName("tmp_customer")
public class Customer {
@TableId(type = IdType.AUTO)
private Integer cid;
private String cname;
private String password;
private String telephone;
private String money;
private Integer version;
@TableField(exist = false)
private List ids;
}
步骤2:编写dao
package com.czxy.mp.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.czxy.mp.domain.Customer;
import org.apache.ibatis.annotations.Mapper;
/**
* Created by 爱吃豆的土豆
*/
@Mapper
public interface CustomerMapper extends BaseMapper {
}
步骤3:编写启动类
package com.czxy.mp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Created by 爱吃豆的土豆
*/
@SpringBootApplication
public class MybatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusApplication.class, args);
}
}
步骤4:编写测试类
package com.czxy;
import com.czxy.mp.MybatisPlusApplication;
import com.czxy.mp.domain.Customer;
import com.czxy.mp.mapper.CustomerMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.util.List;
/**
* Created by 爱吃豆的土豆
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = MybatisPlusApplication.class)
public class TestDemo01 {
@Resource
private CustomerMapper customerMapper;
@Test
public void testFindAll() {
List list = customerMapper.selectList(null);
list.forEach(System.out::println);
}
}
BaseMapper 封装CRUD操作,泛型 T
为任意实体对象
@Test
public void testselectAll(){
List list = customerMapper.selectList(null); //条件查询先传入条件为null
for (Customer c : list) {
System.out.println(c.toString());
}
}
@Test
public void insert(){
Customer customer = new Customer();
customer.setCname("Nihao1");
customer.setPassword("1234");
int i = customerMapper.insert(customer);
System.out.println(i);
}
添加成功返回结果为1,添加失败返回结果为0,否则抛异常
通过id更新
@Test
public void testUpdate() {
Customer customer = new Customer();
customer.setCid(15);
customer.setCname("测试777");
customer.setPassword("777");
// 需要给Customer设置@TableId
customerMapper.updateById(customer);
}
更新所有
@Test
public void testUpdate2() {
Customer customer = new Customer();
customer.setCname("测试777");
customer.setPassword("777");
// 更新所有
customerMapper.update(customer,null);
}
根据id进行删除
@Test
public void testDelete() {
// 需要给Customer设置@TableId
int i = customerMapper.deleteById(11);
System.out.println(i);
}
批量删除
@Test
public void testBatchDelete() {
// 需要给Customer设置@TableId
int i = customerMapper.deleteBatchIds(Arrays.asList(9,10));
System.out.println(i);
}
第一种没有放入任何条件查询:查询所有
@Test
public void selectByMap(){
//简单多条件查询 -使用map设置数据,依次eq查询(等值查询)
HashMap map = new HashMap<>();
List customerList = customerMapper.selectByMap(map);
for (Customer c : customerList) {
System.out.println(c.toString());
}
}
第二种放入条件:按照条件进行查询
@Test
public void selectByMap(){
//简单多条件查询 -使用map设置数据,依次eq查询(等值查询)
HashMap map = new HashMap<>();
map.put("cname","jack");
map.put("password","1234");
List customerList = customerMapper.selectByMap(map);
for (Customer c : customerList) {
System.out.println(c.toString());
}
}
首先先翻译一下wrapper (包装器),不知道大家有没有听过包装类,主要应用在一种模式上面叫装饰 设计模式 里面出现的其实就是包装类。
大家应该见过请求或者响应包装类:分别是
HttpServletRequestWrapper , HttpServletResponseWrapper
像现在在Mybatis-plus 中也用到了wrapper ,它的意思就是把条件查询中的条件给它封装一下
//结合idea向大家介绍一下
idea中 Shift + Ctrl + a 弹出来搜索框:
点进去wrapper中按CTRL + H 查看继承树
Wrapper : 条件构造抽象类,最顶端父类
AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
QueryWrapper : Entity 对象封装操作类,不是用lambda语法
UpdateWrapper : Update 条件封装,用于Entity对象更新操作
AbstractLambdaWrapper : Lambda 语法使用 Wrapper统一处理解析 lambda 获取 column。
LambdaQueryWrapper :看名称也能明白就是用于Lambda语法使用的查询Wrapper
LambdaUpdateWrapper : Lambda 更新封装Wrapper
如果想进行复杂条件查询,那么需要使用条件构造器 Wapper,涉及到如下方法
拼凑条件相关关键字
注意:条件查询里面的条件,column 对应的是数据库中的列,而不是JavaBean中的属性
selectOne 根据条件查询一个,结果:0或者1,如果查询多个异常
三种情况:第一种情况:查询成功一条数据 查询名称为rose的
第二种情况:查询失败,查询到了多个数据 查询名称为发大财,查询到两个
抛出的异常信息(我用中文翻译一下哈哈):期望结果为一个或者为null,但是发现两个
第三种情况:查询为null,不存在该条数据,查询名称大钱,返回结果为null数据库中没有该数据
再练习一个
模糊查询:like 。需要注意的是:之前在Mybatis中模糊查询需要自己手动加入%%
在Mybatis-plus中不需要进行添加在底层中已经进行封装
@Test
public void TestSelectList(){
QueryWrapper queryWrapper = new QueryWrapper<>();
//模糊查询
// queryWrapper.like("cname","c"); //左右加入了%
// queryWrapper.likeLeft("cname","c"); //左加入了%
// queryWrapper.likeRight("cname","c"); //右加入了%
//范围查询 比如年龄大于19的小于20的
queryWrapper.ge("age",19);
queryWrapper.le("age",30);
List customerList = customerMapper.selectList(queryWrapper);
for (Customer c : customerList) {
System.out.println(c.toString());
}
}
测试一个更新:需求更新电话号码为626115的信息,将该数据的名称更新为爱吃豆的土豆
@Test
public void testUpdate(){
Customer customer = new Customer(21, "爱吃豆子的土豆", "123", "123", 1000.0, 0, null, null);
UpdateWrapper updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("telephone",626115);
int update = customerMapper.update(customer, updateWrapper);
System.out.println(update);
}
控制台sql语句执行的流程建议多看看:
主体插件: MybatisPlusInterceptor,该插件内部插件集:
分页插件: PaginationInnerInterceptor(这个怎么理解呢!就是Mybatis-plus提供的内置分页拦截器,就是从到数据库读数据之前,分页拦截器拦截下来,根据分页条件在该执行的sql语句后面加入条件判断)以用来达到我们想要的效果
多租户插件: TenantLineInnerInterceptor
动态表名插件: DynamicTableNameInnerInterceptor
乐观锁插件: OptimisticLockerInnerInterceptor(后文介绍乐观锁插件)
sql性能规范插件: IllegalSQLInnerInterceptor
防止全表更新与删除插件: BlockAttackInnerInterceptor
@Configuration
public class MybatisPlusConfig {
/**
* 配置插件
* @return
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
// 分页插件
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return mybatisPlusInterceptor;
}
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
* @return
*/
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> configuration.setUseDeprecatedExecutor(false);
}
}
@Test
public void testPage(){
//条件查询条件
QueryWrapper queryWrapper = new QueryWrapper();
// queryWrapper.le()
// queryWrapper.like()
//分页查询的条件
int current = 1; // 当前页,相当于pagenum
int size =2;
Page customerPage = new Page<>(current,size);
//分页查询
customerMapper.selectPage(customerPage,queryWrapper);
//处理结果
customerPage.getRecords().forEach(System.out::println);
//分页数据
System.err.println("当前页码:" + customerPage .getCurrent());
System.err.println("每页显示记录数:" + customerPage .getSize());
System.err.println("总页数:" + customerPage .getPages());
System.err.println("总记录数:" + customerPage .getTotal());
System.err.println("是否有下一页:" + customerPage .hasNext());
System.err.println("是否有上一页:" + customerPage .hasPrevious());
}
另外注意一种情况 (当你发现分页信息都无效的时候),首先要考虑有没有配置分页拦截器
@TableName 里面属性还是很多的
(value属性)如果在该注解中只用到了一个属性值value,且是一对,则value可以省略:如果所示
(keepGlobalPrefix属性)大家在以后的开发项目中,会发现一个项目中表都有固定的前缀,既然有前缀那我们就想一想能不能把这个前缀进行抽取出来,放到一个固定的位置,这个属性就起到了该作用:如图所示
value主键名就不多说了,主要说一下type主键类型,也称为主键生成策略
IdType.AUTO,自动增长:
IdType.ASSIGN_UUID
IdType.ASSIGN_ID
1,value:对应表中的列名称
2,fill(在目录(第7)高级中详细讲解)
3,exist:设置是否存储到数据库中:
例如:以后在开发中我们要进行批量删除,为了满足需求我们创建了一个集合或者数组来接收前端传递过来的数据,但这个数据又需要写进数据库中,数据库中也没有对应的列。这样我们就可以用这个属性来控制是否写进数据库中。
注意:Mybatis-plus 中默认一个JavaBean和一张表是一一对应的,如果JavaBean中多出一个成员变量,它就去表里面找对应的列,没有的话就报错,这样就可以用这个属性进行声明
讲一下配置在yml文件中Mybatis-plus的常见配置(我这里是springboot项目)
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #输出日志
map-underscore-to-camel-case: true #驼峰命名
global-config:
db-config:
id-type: auto #全局配置,id自动增强
table-prefix: tmp_ #表名前缀
type-aliases-package: com.czxy.mp.domain #别名包扫描路径
mapper-locations: classpath*:/mapper/**/*.xml #映射文件位置
日志介绍(给大家讲解了各种日志):
开启驼峰(用图和大家解释):
全局配置主键策略和全局配置表名的前缀 :
xml相关配置,整合XML版本开发 (重要):
mapper-locations: 就是mapper映射的XML文件位置(如下图)。因为经常用注解开发很少写XML版开发,所以不怎么写。
type-aliases-package:这个应用场景打个比方就是,如果以后开发中你做了十几个查询功能,返回类型都是同一个,这样每一次都全部输入显得很麻烦,我们可以通过这个配置进行起别名操作。(详细讲解如图)
XML文件中内容(官方模板):
项目中经常会遇到一些数据,每次都使用相同的方式填充,例如记录的创建时间,更新时间等。
我们可以使用MyBatis Plus的自动填充功能,完成这些字段的赋值工作:
7.1.1:原理
实现元对象处理器接口:com.baomidou.mybatisplus.core.handlers.MetaObjectHandler
,确定填充具体操作
注解填充字段:@TableField(fill = ...)
确定字段填充的时机
FieldFill.INSERT:插入填充字段
FieldFill.UPDATE:更新填充字段
FieldFill.INSERT_UPDATE:插入和更新填充字段
解释一下:这个自动填充就是相当于为程序添加一个拦截器,当用户改变自己的信息的时候,拦截一下自动把更改的时间进行装填到数据库中。在以后工作中会经常用到
步骤一:修改表添加字段:为表添加两个字段
alter table tmp_customer add column create_time date;
alter table tmp_customer add column update_time date;
步骤二:修改JavaBean
package com.czxy.mp.domain;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.util.Date;
/**
* Created by 爱吃豆的土豆.
*/
@Data
@TableName("tmp_customer")
public class Customer {
@TableId(type = IdType.AUTO)
private Integer cid;
private String cname;
private String password;
private String telephone;
private String money;
@TableField(value="create_time",fill = FieldFill.INSERT)
private Date createTime;
@TableField(value="update_time",fill = FieldFill.UPDATE)
private Date updateTime;
}
package com.czxy.mp.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* @author 爱吃豆的土豆
*/
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
/**
* 插入填充
* @param metaObject
*/
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("createTime", new Date(), metaObject);
}
/**
* 更新填充
* @param metaObject
*/
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime", new Date(), metaObject);
}
}
步骤四:测试
@Test
public void testInsert() {
Customer customer = new Customer();
customer.setCname("测试888");
customerMapper.insert(customer);
}
@Test
public void testUpdate() {
Customer customer = new Customer();
customer.setCid(11);
customer.setTelephone("999");
customerMapper.updateById(customer);
}
基于数据库
乐观锁:数据不同步不会
发生。读锁。
乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。
悲观锁:数据不同步肯定
发生。写锁。
悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
目的:数据必须同步。当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:
取出记录时,获取当前version
更新时,带上这个version
执行更新时, set version = newVersion where version = oldVersion
如果version不对,就更新失败
步骤:
步骤1:环境准备(表version字段、JavaBean versoin属性、必须提供默认值)
步骤2:使用乐观锁版本控制 @Version
步骤3:开启乐观锁插件配置
package com.czxy.mp.config;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* Created 爱吃豆的土豆.
*/
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
/**
* 插入填充
* @param metaObject
*/
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("createTime", new Date(), metaObject);
this.setFieldValByName("version", 1, metaObject);
}
/**
* 更新填充
* @param metaObject
*/
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime", new Date(), metaObject);
}
}
/**
* Created 爱吃豆的土豆.
*/
@Configuration
public class MybatisPlusConfig {
*/
/**
* 配置插件
* @return
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
// 分页插件
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// 乐观锁
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mybatisPlusInterceptor;
}
}
步骤五:测试
先添加一条,保证version有数据
在更新该条
@Test
public void testUpdate() {
Customer customer = new Customer();
customer.setCid(14);
customer.setCname("测试999");
// 与数据库中数据一致,将更新成功,否则返回失败。
customer.setVersion(1);
int i = customerMapper.updateById(customer);
System.out.println(i);
}
支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
整数类型下 newVersion = oldVersion + 1
newVersion
会回写到 entity
中
仅支持 updateById(id)
与 update(entity, wrapper)
方法
在 update(entity, wrapper)
方法下, wrapper
不能复用!!!
数据库表的version字段,必须有默认值(SQL语句默认值、或MyBatisPlus自动填充)
在进行更新操作时,必须设置version值,否则无效。
逻辑删除,也称为假删除。就是在表中提供一个字段用于记录是否删除,实际该数据没有被删除。(像大部分用户信息,如果你联系客服让他注销的话,只是客观意义上找不到用户信息,实际上还是存在的,只是进行了逻辑删除。)
步骤:
步骤一:环境(表提供字段deleted、JavaBean属性 deleted、填充默认值0)
步骤二:修改JavaBean,添加注解 @TableLogic
步骤一:修改表结构添加deleted字段
步骤二:修改JavaBean,给deleted字段添加@TableLogic
/**
* Created 爱吃豆的土豆.
*/
@Data
@TableName("tmp_customer")
public class Customer {
@TableId(type = IdType.AUTO)
private Integer cid;
private String cname;
private String password;
private String telephone;
private String money;
@Version
@TableField(fill = FieldFill.INSERT)
private Integer version;
@TableLogic
@TableField(fill = FieldFill.INSERT)
private Integer deleted;
}
步骤三:添加数据时,设置默认“逻辑未删除值”
/**
* Created 爱吃豆的土豆
*/
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
/**
* 插入填充
* @param metaObject
*/
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("createTime", new Date(), metaObject);
this.setFieldValByName("version", 1, metaObject);
this.setFieldValByName("deleted", 0, metaObject);
}
/**
* 更新填充
* @param metaObject
*/
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime", new Date(), metaObject);
}
}
步骤四:测试
@Test
public void testDelete() {
// 删除时,必须保证deleted数据为“逻辑未删除值”
int i = customerMapper.deleteById(12);
System.out.println(i);
}
如果使用逻辑删除,将delete语句,修改成了update,条件 where deleted=0
同时,查询语句自动追加一个查询条件 WHERE deleted=0
。如果查询没有数据,检查deleted字段的值。
如果使用了全局配置,可以不使用注解@TableLogic
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted # 局逻辑删除的实体字段名
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
问题:进行逻辑删除后的数据,如何恢复(recovery)?
方案1:使用sql具有,更新deleted=0即可
UPDATE `tmp_customer` SET `deleted`='0' WHERE `cid`='13';
方案2:直接使用update语句,==不能==解决问题。
@Test
public void testUpdate() {
Customer customer = new Customer();
customer.setCid(13);
customer.setDeleted(0);
//更新
customerMapper.updateById(customer);
}
方案3:修改Mapper,添加 recoveryById 方法,进行数据恢复。
@Mapper
public interface CustomerMapper extends BaseMapper {
@Update("update tmp_customer set deleted = 0 where cid = #{cid}")
public void recoveryById(@Param("cid") Integer cid);
}
标准service:接口 + 实现
service接口
public interface CustomerService extends IService {
}
service实现类
@Service
@Transactional
public class CustomerServiceImpl extends ServiceImpl implements CustomerService {
}
查询所有
添加
修改
删除
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Day62MybatisPlusApplication.class)
public class TestDay62CustomerService {
//导入通用service
@Resource
private CustomerService customerService;
//测试查询所有
@Test
public void testSelectList() {
List list = customerService.list();
list.forEach(System.out::println);
}
//测试添加
@Test
public void testInsert() {
Customer customer = new Customer();
customer.setCname("张三");
customer.setPassword("9999");
// 添加
customerService.save(customer);
}
//测试更新
@Test
public void testUpdate() {
Customer customer = new Customer();
customer.setCid(14);
customer.setCname("777");
customer.setPassword("777");
customerService.updateById(customer);
}
//测试添加或更新
@Test
public void testSaveOrUpdate() {
Customer customer = new Customer();
customer.setCid(15);
customer.setCname("999");
customer.setPassword("99");
customerService.saveOrUpdate(customer);
}
//测试通过id进行删除
@Test
public void testDelete() {
customerService.removeById(15);
}
该功能依赖 p6spy
组件,完美的输出打印 SQL 及执行时长
p6spy 依赖引入
p6spy
p6spy
3.9.1
核心yml配置
spring:
datasource:
# p6spy 提供的驱动代理类,
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
# url 固定前缀为 jdbc:p6spy,跟着冒号为对应数据库连接地址
url: jdbc:p6spy:mysql://127.0.0.1:3306...
#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
为了保护数据库配置及数据安全,在一定的程度上控制开发人员流动导致敏感信息泄露
步骤:
步骤1:使用 AES 工具类,生成秘钥
。
步骤2:使用 AES工具类,根据步骤1生成的秘钥对敏感信息进行加密
步骤3:设置加密后的配置信息
步骤4:启动服务时,使用秘钥
public class TestAES {
@Test
public void testAes() {
String randomKey = AES.generateRandomKey();
String url = "jdbc:p6spy:mysql://127.0.0.1:3306/zx_edu_teacher?useUnicode=true&characterEncoding=utf8";
String username = "root";
String password = "1234";
String urlAES = AES.encrypt(url, randomKey);
String usernameAES = AES.encrypt(username, randomKey);
String passwordAES = AES.encrypt(password, randomKey);
System.out.println("--mpw.key=" + randomKey);
System.out.println("mpw:" + urlAES);
System.out.println("mpw:" + usernameAES);
System.out.println("mpw:" + passwordAES);
}
}
// Jar 启动参数( idea 设置 Program arguments , 服务器可以设置为启动环境变量 )
//--mpw.key=fddd2b7a67460e16
//mpw:7kSEISvq3QWfnSh6vQZc2xgE+XF/sJ0WS/sgGkYpCOTQRjO1poLi3gfmGZNOwKzfqZUec0odiwAdmxcS7lfueENGIx8OmIe//d9imrGFpnkrf8jNSHdzfNPCUi3MbmUb
//mpw:qGbCMksqA90jjiGXXRr7lA==
//mpw:xKG9GABlywqar6CGPOSJKQ==