前言
本文采用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.DruidDataSourceserver: port: 80
-
新建mappe进行操作
@Mapperpublic 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代码
@AutowiredItEmployeedao etEmployeedao;/** * 按id查询 */@Testvoid selectids(){ ItEmployee itEmployee = new ItEmployee(); itEmployee.setId(1); System.out.println(etEmployeedao.getByIiEmployees(itEmployee));}/** * 按id批量查询 */@Testvoid selectidLists(){ ArrayList<Integer> integers = new ArrayList<>(); integers.add(1); integers.add(2); integers.add(3); System.out.println(etEmployeedao.getByIiEmployeesList(integers));}/** * 按多个参数查询map */@Testvoid selectidMaps(){ HashMap<String, Object> map = new HashMap<>(); map.put("id","1"); map.put("age","18"); System.out.println(etEmployeedao.getByIiEmployeesMap(map));}/** * 新增 */@Testvoid 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("新增成功!"); }}/** * 编辑 */@Testvoid 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("修改成功!"); }}/** * 删除 */@Testvoid 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基类 */@Servicepublic 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
@ResourceEmployeeService employeeService; /** * service方法根据id查询 */@Testvoid servicetest(){ ItEmployee byId = employeeService.getById(1); System.out.println(byId);}/*** * service方法他会首先查询,如查询没有则新增有则修改 */@Testvoid 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文件官网有提供的
@Configurationpublic 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>
@Mapperpublic 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.DruidDataSourcelogging: level: root: info com.tulingxueyuan: debugmybatis-plus: global-config: db-config: logic-delete-field: enabled #逻辑删除的字段 logic-delete-value: 0 #已删除数据 logic-not-delete-value: 1 #有效数据 id-type: autoserver: 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;@SpringBootApplicationpublic 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的一个插件 添加配置文件
@Configurationpublic class mybatisplusConfig { //最新版 @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); //分页插件 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));// // 乐观锁的插件 interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; }}
只需要实现MetaObjectHandler就可以了
@Componentpublic 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
/*** * 乐观锁 */@Testvoid 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:wqprojectjavaprojectspringbootspringboot_mybatis-plusspringboot_mybatisplussrcmain
esourcesmappexxr")); // 设置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等都帮我生成好了真是一个利器呀!
总结
以上就是今天要讲的内容了,钟对mybatisplus的具体操作使用,需要内容源码的可私信我,就到这了有问题再评论区提出来多交流学习。