文章目录
前言
本文采用Springboot集成mybatis-plus,记录了集成的全部的详细操作。希望大家看到之后一起交流学习
一、mybatis-plus是什么?
MyBatisPlus(简称 MP)是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
愿景:
我们的愿景是成为 MyBatis 最好的搭档,就像 魂斗罗 中的 1P、2P,基友搭配,效率翻倍。
特性
-
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
-
损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
-
强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
-
支持 Lambda 形式调用:通过 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 操作智能分析阻断,也可自定义拦截规则,预防误操作
官网:MyBatis-Plus
注:
ps:切记不可再在pom.xml文件中引入mybatis与mybatis-spring的maven依赖,这一点,mybatis-plus的官方文档中已经说明的很清楚了
二、使用步骤
1.快速入门
mybatis-plus 快速使用
-
导入相关依赖
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- mybatis-plus不需要再额外引入mybatis了-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.18</version>
</dependency>
2.创建数据表并编写Entity实体(刚入门测试,后续代码生成器可以全部生成controller、service、mapper、实体等都可以生成)
CREATE TABLE it_employee (
id INT(11) PRIMARY KEY auto_increment,
emp_name VARCHAR(50),
emp_email VARCHAR(50),
gender CHAR(1),
age INT(10)
);
INSERT INTO it_employee VALUES (NULL,'张三','zhangsan@163.com','0',18);
INSERT INTO it_employee VALUES (NULL,'李四','lisi@163.com','1',21);
INSERT INTO it_employee VALUES (NULL,'王五','wangwu@163.com','1',19);
INSERT INTO it_employee VALUES (NULL,'赵六','zhaoliu@163.com','0',17);
实体采用的lombok
import com.baomidou.mybatisplus.annotation.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.Date;
/*
* MybatisPlus会默认使用实体类的类名到数据中找对应的表.
* 所以表名和实体类名不一致时需要使用@TableName
*/
@Component
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "it_employee")
public class ItEmployee {
/*
* @TableId:
* value: 指定表中的主键列的列名, 如果实体属性名与列名一致,可以省略不指定.
* type: 指定主键策略.
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@TableField(value = "emp_name")
private String empName;
@TableField(value = "emp_email")
private String empEmail;
private String gender;
private Integer age;
}
上面实体类中使用到了常用的注解,那我们先来介绍一下常用的注解
mybatis-plus中常用的注解
@TableName:对数据表名注解
@TableId:表主键标识
@TableId(value = "id", type = IdType.AUTO):自增
@TableId(value = "id", type = IdType.ID_WORKER_STR):分布式全局唯一ID字符串类型
@TableId(value = "id", type = IdType.INPUT):自行输入
@TableId(value = "id", type = IdType.ID_WORKER):分布式全局唯一ID 长整型类型
@TableId(value = "id", type = IdType.UUID):32位UUID字符串
@TableId(value = "id", type = IdType.NONE):无状态
@TableField:表字段标识
@TableField(exist = false):表示该属性不为数据库表字段,但又是必须使用的。
@TableField(exist = true):表示该属性为数据库表字段。
@TableField(condition = SqlCondition.LIKE):表示该属性可以模糊搜索。
@TableField(fill = FieldFill.INSERT):注解填充字段 ,生成器策略部分也可以配置!
@FieldStrategy:
@FieldFill
@Version:乐观锁注解、标记
@EnumValue:通枚举类注解
@TableLogic:表字段逻辑处理注解(逻辑删除)
@SqlParser:租户注解
@KeySequence:序列主键策略
常用的就三个:
@TableName
@TableId
@TableField
查看更多注解以及详解,请移步至官网:
https://mybatis.plus/guide/annotation.html
3.配置yml基本的数据库连接信息以及数据源设置,这里数据源采用的是druid
# 数据源
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/stupubdemo?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&autoReconnect=true&failOverReadOnly=false&maxReconnects=10
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
server:
port: 80
-
新建mappe进行操作
@Mapper
public interface ItEmployeedao extends BaseMapper<ItEmployee> {
}
BaseMapper<T>的介绍:
只需要继承BaseMapper就好无需编写其他方法,在使用Mybatis-Plus是,核心操作类是BaseMapper接口,其最终也是利用的Mybatis接口编程的实现机制,其默认提供
了一系列的增删改查的基础方法,并且开发人员对于这些基础操作不需要写SQL进行处理操作(Mybatis提供的机制就是 需要开发人员在mapper.xml中提供sql语句),那样我们可以猜测肯定是Mybatis-Plus完成了BaseMapper接口提供的 方法的SQL语句的生成操作
BaseMapper中提供的方法:
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import org.apache.ibatis.annotations.Param;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能
* <p>这个 Mapper 支持 id 泛型</p>
*
* @author hubin
* @since 2016‐01‐23
*/
public interface BaseMapper<T> extends Mapper<T> {
/**
* 插入一条记录
*
* @param entity 实体对象
*/
int insert(T entity);
/**
* 根据 ID 删除
*
* @param id 主键ID
*/
int deleteById(Serializable id);
/**
* 根据 columnMap 条件,删除记录
*
* @param columnMap 表字段 map 对象
*/
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
/**
* 根据 entity 条件,删除记录
*
* @param wrapper 实体对象封装操作类(可以为 null)
*/63 int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
/**
* 删除(根据ID 批量删除)
*
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
/**
* 根据 ID 修改
*
* @param entity 实体对象
*/
int updateById(@Param(Constants.ENTITY) T entity);
/**
* 根据 whereEntity 条件,更新记录
*
* @param entity 实体对象 (set 条件值,可以为 null)
* @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
*/
int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);
/**
* 根据 ID 查询
*
* @param id 主键ID
*/
T selectById(Serializable id);
/**
* 查询(根据ID 批量查询)
*
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
/**
* 查询(根据 columnMap 条件)
*
* @param columnMap 表字段 map 对象
*/
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
/**
* 根据 entity 条件,查询一条记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 Wrapper 条件,查询总记录数117 *
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 entity 条件,查询全部记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 Wrapper 条件,查询全部记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 Wrapper 条件,查询全部记录
* <p>注意: 只返回第一个字段的值</p>
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 entity 条件,查询全部记录(并翻页)
*
* @param page 分页查询条件(可以为 RowBounds.DEFAULT)
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
<E extends IPage<T>> E selectPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 根据 Wrapper 条件,查询全部记录(并翻页)
*
* @param page 分页查询条件
* @param queryWrapper 实体对象封装操作类
*/
<E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
}
-
测试类编写CRUD代码
@Autowired
ItEmployeedao etEmployeedao;
/**
* 按id查询
*/
@Test
void selectids(){
ItEmployee itEmployee = new ItEmployee();
itEmployee.setId(1);
System.out.println(etEmployeedao.getByIiEmployees(itEmployee));
}
/**
* 按id批量查询
*/
@Test
void selectidLists(){
ArrayList<Integer> integers = new ArrayList<>();
integers.add(1);
integers.add(2);
integers.add(3);
System.out.println(etEmployeedao.getByIiEmployeesList(integers));
}
/**
* 按多个参数查询map
*/
@Test
void selectidMaps(){
HashMap<String, Object> map = new HashMap<>();
map.put("id","1");
map.put("age","18");
System.out.println(etEmployeedao.getByIiEmployeesMap(map));
}
/**
* 新增
*/
@Test
void insert(){
ItEmployee itEmployee = new ItEmployee();
itEmployee.setAge(12);
itEmployee.setEmpEmail("123@163.com");
itEmployee.setEmpName("张三丰");
itEmployee.setGender("0");
int insert = etEmployeedao.insert(itEmployee);
if (insert>0){
System.out.println("新增成功!");
}
}
/**
* 编辑
*/
@Test
void update(){
ItEmployee itEmployee = new ItEmployee();
itEmployee.setId(10);
itEmployee.setAge(10);
itEmployee.setEmpEmail("123456@163.com");
itEmployee.setEmpName("张丰三");
itEmployee.setGender("0");
int insert = etEmployeedao.update(itEmployee);
if (insert>0){
System.out.println("修改成功!");
}
}
/**
* 删除
*/
@Test
void del(){
ItEmployee itEmployee = new ItEmployee();
itEmployee.setId(7);
int insert = etEmployeedao.del(itEmployee);
if (insert>0){
System.out.println("删除成功!");
}
}
以上就是快速使用,基本操作都可以满足了,而且带条件的一些sql等都正好,上面是使用的mapper层直接使用,但是我们在实际开发中一般都会使用service,那么service层代码来了
2.复杂场景的使用
实现带service层的dai复杂条件的各种操作
-
以上快速使用的基础上,新建EmployeeService
import com.baomidou.mybatisplus.extension.service.IService;
import com.wq.mybatisplusdemo.demo.entity.ItEmployee;
public interface EmployeeService extends IService<ItEmployee> {
}
新建EmployeeServiceimpl
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wq.mybatisplusdemo.demo.dao.ItEmployeedao;
import com.wq.mybatisplusdemo.demo.entity.ItEmployee;
import com.wq.mybatisplusdemo.demo.service.EmployeeService;
import org.springframework.stereotype.Service;
/***
* service 实现类继承map提供通用的service基类
*/
@Service
public class EmployeeServiceimpl extends ServiceImpl<ItEmployeedao, ItEmployee> implements EmployeeService {
}
就这么简单,service层就这样就行了,里面也有许多的方法直接使用
就可以准备测试了,但是在测试之前不得不提的条件构造器---Wrapper
wrapper及其子类介绍
-
Wrapper :条件构造抽象类,最顶端父类,抽象类中提供3个方法以及其他方法
(2)AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件,QueryWrapper(LambdaQueryWrapper) 和
UpdateWrapper(LambdaUpdateWrapper) 的父类用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where
条件
(3)AbstractWrapper比较重要,里面的方法需要重点学习.
该抽象类提供的重要方法如下:(3)AbstractLambdaWrapper : Lambda 语法使用 Wrapper统一处理解析 lambda 获取 column。
(4)LambdaQueryWrapper :看名称也能明白就是用于Lambda语法使用的查询Wrapper
(5)LambdaUpdateWrapper : Lambda 更新封装Wrapper
(6)QueryWrapper : Entity 对象封装操作类,不是用lambda语法,自身的内部属性 entity 也用于生成 where 条件
该类的重要方法:
select方法
1 select(String... sqlSelect)
2 select(Predicate<TableFieldInfo> predicate)
3 select(Class<T> entityClass, Predicate<TableFieldInfo> predicate)
4 /*
5 例: select("id", "name", "age")
6 例: select(i ‐> i.getProperty().startsWith("test"))
7 */
(7)UpdateWrapper : Update 条件封装,用于Entity对象更新操作.
该类主要有以下三个重要的方法:
set方法1 set(String column, Object val)
2 set(boolean condition, String column, Object val)
3 /*
4 SQL SET 字段
5 例: set("name", "老李头")
6 例: set("name", "")‐‐‐>数据库字段值变为空字符串
7 例: set("name", null)‐‐‐>数据库字段值变为null
8 说明:boolean condition为控制该字段是否拼接到最终的sql语句中
9 */
setSql方法
1 setSql(String sql)
2 /*
3 设置 SET 部分 SQL
4 例: setSql("name = '老李头'")
5 */
3.2.带条件的crud实验
(1)带条件的查询
1
2 // 根据 entity 条件,查询一条记录
3 T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
4
5 // 根据 entity 条件,查询全部记录
6 List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
7
8 // 根据 Wrapper 条件,查询全部记录
9 List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
10 // 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值
11 List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
12
13 // 根据 entity 条件,查询全部记录(并翻页)
14 IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
15 // 根据 Wrapper 条件,查询全部记录(并翻页)
16 IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWr
apper);
17 // 根据 Wrapper 条件,查询总记录数
18 Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
(2)带条件的更新
1 @Test
2 void update() {
3 UpdateWrapper<Employee> updateWrapper=new UpdateWrapper<Employee>();
4 updateWrapper.eq("last_name", "lili").eq("age", 18).set("id", 100).set(false, "email", "000@qq.com");
5 empolyeeMapper.update(employee, updateWrapper);
6
7 }
8 }
其中set("id", 100).set(false, "email", "000@qq.com");中email属性设置为false,从执行的sql可以看出,设置为false不会
拼接到最终的执行sql中
(3)带条件的删除
1 // 根据 entity 条件,删除记录2 int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
3
4 // 根据 columnMap 条件,删除记录
5 int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
测试service
@Resource
EmployeeService employeeService;
/**
* service方法根据id查询
*/
@Test
void servicetest(){
ItEmployee byId = employeeService.getById(1);
System.out.println(byId);
}
/***
* service方法他会首先查询,如查询没有则新增有则修改
*/
@Test
void saveOrUpdate(){
ItEmployee itEmployee = new ItEmployee(6,"111","123@qq.com","0",12,"1",null,new Date(),null);
employeeService.saveOrUpdate(itEmployee);
}
/***
* 分页 需要先导入插件,需要新建config文件官网有提供
*/
@Test
void page(){
IPage<ItEmployee> ipage= new Page<>(1,2);
IPage<ItEmployee> page=employeeService.page(ipage);
List<ItEmployee> pageRecords = page.getRecords();
System.out.println(page.getSize());
System.out.println(page.getTotal());
System.out.println(pageRecords);
}
/***
* xml分页
*/
@Test
void xmlpage(){
IPage<ItEmployee> ipage=new Page<>(1,2);
List<ItEmployee> employees = itEmployeeService.getbygender(ipage, "1").getRecords();
for (ItEmployee employee : employees) {
System.out.println(employee);
}
}
@Test
void queryWrapper(){
QueryWrapper<ItEmployee> queryWrapper=new QueryWrapper<>();
queryWrapper
.select("emp_name","emp_email","age")
.eq("emp_name","李四");
System.out.println(employeeService.list(queryWrapper));
}
/***
* 根据 entity 条件,查询一条记录
*/
@Test
void QueryWrappertest(){
ItEmployee employee = new ItEmployee();
employee.setEmpName("王五");
employee.setGender("0");
ItEmployee selectone = itEmployeeService.selectone(employee);
System.out.println(selectone);
}
/***
* 设置带多条件的修改
*/
@Test
void UpdateWrappertest(){
ItEmployee employee = new ItEmployee();
employee.setEmpName("李四");
employee.setGender("1");
UpdateWrapper<ItEmployee> updateWrapper=new UpdateWrapper<>();
// 查询条件emp_name为王五的和gender为1的 set为修改的 set(false, "email", "000@qq.com");中email属性设置为false,从执行的sql可以看出,设置为false不会
// 拼接到最终的执行sql中
// updateWrapper.eq("emp_name",employee.getEmpName()).eq("gender",employee.getGender()).set("age",13).set(false,"email","wangwu@163.com");
updateWrapper
.lambda()
.set(ItEmployee::getAge,24)
.eq(ItEmployee::getEmpName,"李四")
.eq(ItEmployee::getGender,"1")
;
int selectone = itEmployeeService.updateEntityparams(employee, updateWrapper);
if (selectone>0){
System.out.println("修改成功!");
}
}
/**
* 设置带多条件的删除
*/
@Test
void DelWrappertest(){
QueryWrapper<ItEmployee> employeeQueryWrapper=new QueryWrapper<>();
employeeQueryWrapper.eq("emp_name","王五").eq("gender","1");
int delEntityparams = itEmployeeService.DelEntityparams(employeeQueryWrapper);
if (delEntityparams>0){
System.out.println("删除成功!");
}
}
上面test类中有提到分页
他的分页需要新建config文件官网有提供的
@Configuration
public class mybatisplusConfig {
//最新版
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
xml分页的使用
需要新建xml文件,有时候在业务场景比较复杂的情况下是可以自己编写xml进行分页编写的,他也保留mybatis作为半自动化的一款交互
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.wq.mybatisplusdemo.demo.dao.ItEmployeedao" >
<select id="getbygender" resultType="com.wq.mybatisplusdemo.demo.entity.ItEmployee">
select * from it_employee where gender=#{gender}
</select>
</mapper>
@Mapper
public interface ItEmployeedao extends BaseMapper<ItEmployee> {
/***
* xml分页
* @param page
* @param
* @return
*/
IPage<ItEmployee> getbygender(IPage page,String gender);
}
扩展
全局ID生成策略
在全局配置文件中: 就不需要再每个Pojo主键上配置了
mybatis‐plus:
global‐config:
db‐config:
id‐type: auto
逻辑删除
物理删除: 在删除的时候直接将数据从数据库干掉DELTE
逻辑删除: 从逻辑层面控制删除,通常会在表里添加一个逻辑删除的字段比如 enabled 、is_delete ,数据默认是有效的(值为1),
当用户删除时将数据修改UPDATE 0, 在查询的时候就只查where enabled=1.
1. 需要添加逻辑删除的字段
2. 局部单表逻辑删除,需要在对应的pojo类加入对应的逻辑删除标识字段
@TableLogic // 代表逻辑删除
private Integer flag;
public Integer getFlag() {
return flag;
}
全局逻辑删除配置, 如果进行了全局逻辑删除配置并且指定了,就可以不用在每个pojo类中配置了@TableLogic
mapper-locations: classpath:/mappexxr/*.xml
global‐config:
db‐config:
logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 0 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 1 # 逻辑未删除值(默认为 0)
id‐type: auto
当执行删除, 将会把逻辑删除字段进行修改当执行查询,会自动查询有效数据 where flag=1
执行 SQL 分析打印
<!--sql性能分析日志-->
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.9.1</version>
</dependency>
添加p6spy : spy.properties
#3.2.1以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6Ou
tageFactory
#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 秒24 outagedetectioninterval=2
yml的连接信息需要改动
# 数据源
spring:
datasource:
username: root
password: wq123
url: jdbc:p6spy:mysql://localhost:3306/stupubdemo?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&autoReconnect=true&failOverReadOnly=false&maxReconnects=10
driver-class-name: com.p6spy.engine.spy.P6SpyDriver #com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
效果:
数据安全保护
防止删库跑路
1.得到16位随机秘钥
@Test
void test(){// 生成 16 位随机 AES 密钥
String randomKey = AES.generateRandomKey();
System.out.println(randomKey);
}
运行结果的 : da12166c7db8a58f
-
根据秘钥加密 数据库连接信息
@Test
void test(){
String url = AES.encrypt("jdbc:mysql://localhost: 3306/mybatisplus?characterEncoding=utf8&useSSL=false
&serverTimezone=UTC&" , "da12166c7db8a58f");
String uname = AES.encrypt("root" , "da12166c7db8a58f");
String pwd = AES.encrypt("123456" , "da12166c7db8a58f");
System.out.println(url);
System.out.println(uname);
System.out.println(pwd);11 }
把数据库的url user pwd等进行加密
3.修改生产的配置文件prod 注意要mpw:开头
# 数据源
spring:
datasource:
username: mpw:uO+56CtZiH7WvW0Uttle0w==
password: mpw:+R96zspi9RpHpagOrFo7jw==
url: mpw:O97zTPbK6kRCnbNRHvNclC9A1eohqPwv40CKLaI+w678j4njvEsuoTuMkr/hXULdscaFPDQxxFRpLRELduLEsEHCOPEp6JV1JQi+vbgcQg95zCKQWC5PIyXvitTpDCJsIjwlH62hxTochApIXr+6HTNDXfXIj2aOeNMfy87jFx7Ax0fqc/NrAIIka4+Mh7EFeZdbbYvHq0dlzpmNkXhJCLF1upppRVhiSnupk3ANeQ0=
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
logging:
level:
root: info
com.tulingxueyuan: debug
mybatis-plus:
global-config:
db-config:
logic-delete-field: enabled #逻辑删除的字段
logic-delete-value: 0 #已删除数据
logic-not-delete-value: 1 #有效数据
id-type: auto
server:
port: 8010
-
在springboot启动类中添加测试方法
import com.wq.mybatisplusdemo.demo.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
static EmployeeService employeeService;
@Autowired
public void setEmployeeService(EmployeeService employeeService) {
DemoApplication.employeeService = employeeService;
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
// 测试数据安全保护下 部署上线能否正确连接数据库
System.out.println(employeeService.getById(1));
}
}
4.使用maven进行package进行打包
注意打包的时候你的本地运行还是开发环境,不然打不好的,
默认指向开发
4.打开你打完包后的目录然后进行cmd 在部署的时候需要解密 java -jar xxxx.jar --mpw.key=你的16位随机秘钥 --spring.profiles.active=prod
查询出数据代表部署成功
乐观锁插件使用
第一:什么是乐观锁
悲观锁:悲观锁,正如其名,具有强烈的独占和排他特性。它指的是对数据被外界(包括本系统当前的其他事
务,以及来自外部系统的事务处理)修改持保守态度。因此,在整个数据处理过程中,将数据处于锁定状态。
假设功能并发量非常大,就需要使用synchronized来处理高并发下产生线程不安全问题, 会使其他线程进行挂起等待从
而影响系统吞吐量
乐观锁:乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正
式对数据的冲突与否进行检测,如果发现冲突了,则返回给用户错误的信息,让用户决定如何去做。乐观锁适用于读操作多的场
景,这样可以提高程序的吞吐量。 假设功能产生并发几率极少,采用乐观锁版本机制对比, 如果有冲突 返回给用户错
误的信息
第二:为什么需要锁(并发控制)
在多用户环境中,在同一时间可能会有多个用户更新相同的记录,这会产生冲突。这就是著名的并发性问题
丢失更新:一个事务的更新覆盖了其它事务的更新结果,就是所谓的更新丢失。例如:用户1把值从500改为
8000,用户B把值从500改为200,则多人同时提交同一条记录,后提交的把之前的提交数据覆盖。
脏读:当一个事务读取其它完成一半事务的记录时,就会发生脏读。例如:用户A,B看到的值都是500,用户B
把值改为200,用户A读到的值仍为500。
针对一种问题的解决方案,为解决问题而生的。解决什么问题呢?主要是解决丢失更新问题如下图理解
为了解决这些并发带来的问题。 我们需要引入并发控制机制。
第三:乐观锁使用MyBatisPlus的解决方式
由于锁这个字眼我们需要在数据库加个字段“
version”来控制版本
在类中加个属性
@Version //这就是控制版本的
@TableField(fill = FieldFill.INSERT) //这个方便在添加的时候设置版本初始为1
private Integer version; //版本的字段
下面这个也是MyBatisPlus的一个插件 添加配置文件
@Configuration
public class mybatisplusConfig {
//最新版
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// // 乐观锁的插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
只需要实现MetaObjectHandler就可以了
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
// 插入时:创建时间和修改时间
this.setFieldValByName("createDate",new Date(),metaObject);
this.setFieldValByName("modifyDate",new Date(),metaObject);
//这里的“version”就是指定的字段,设置初始值为1,之后每修改一次+1
this.setFieldValByName("version",1,metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("modifyDate",new Date(),metaObject);
}
}
接下来在做增加数据的时候,调用insert添加方法就可以了。
修改的时候呢,我们需要先查人后再做修改,因为我们为了防止问题的发生,需要先去查询版本号比对才进行后续操 作!!
test
/***
* 乐观锁
*/
@Test
void testCAs(){
ItEmployee employee =employeeService.getById(1);
employee.setAge(100);
ItEmployee employee2 =employeeService.getById(1);
employee2.setAge(80);
if (employeeService.updateById(employee)){
System.out.println("更新成功");
}
if (!employeeService.updateById(employee2)){
System.out.println("请稍后重试!");
}
}
效果:
4.代码生成器
新建类
public static void main(String[] args) {
String userName="root";
String password="wq123";
String url="jdbc:mysql://localhost:3306/stupubdemo?useSSL=false&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&autoReconnect=true&failOverReadOnly=false&maxReconnects=10";
FastAutoGenerator.create(url, userName, password)
.globalConfig(builder -> {
builder.author("Qi.Wang") // 设置作者
.enableSwagger() // 开启 swagger 模式
.fileOverride() // 覆盖已生成文件
.outputDir("D://logs")//生成的项目目录
.dateType(DateType.TIME_PACK)
.commentDate("yyyy-MM-dd"); // 指定输出目录
})
.packageConfig(builder -> {
builder.parent("com.wq.mybatisplusdemo.demo") // 设置父包名
.moduleName("wms") // 设置父包模块名
.entity("entity")
.service("service")
.serviceImpl("service.impl")
.mapper("mapper")
.xml("mapper.xml")
.controller("controller")
.other("other")
.pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D:\\wqproject\\javaproject\\springboot\\springboot_mybatis-plus\\springboot_mybatisplus\\src\\main\\resources\\mappexxr")); // 设置mapperXml生成路径
})
.strategyConfig(builder -> {
builder.addInclude("it_employee") // 设置需要生成的表名
.addTablePrefix("it_"); // 设置过滤表前缀
})
.strategyConfig(builder -> {
builder.entityBuilder()
.enableTableFieldAnnotation()
.versionColumnName("version")
.versionPropertyName("version")
.logicDeleteColumnName("flag")
.logicDeletePropertyName("flag")
.addSuperEntityColumns("id","create_by","create_date","modify_by","modify_date")
.idType(IdType.AUTO)
.formatFileName("%sEntity");
})
.strategyConfig(builder -> {
builder.serviceBuilder()
.formatServiceFileName("%sService")
.formatServiceImplFileName("%sServiceImpl");
})
.strategyConfig(builder -> {
builder.mapperBuilder()
.enableMapperAnnotation();
})
// .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}
具体还想生成一些内容可参考官网
效果:
controller、entity mapper service等都帮我生成好了真是一个利器呀!
总结
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。