MyBatisPlus极快入门
一、MyBatisPlus介绍
MyBatisPlus(简称 MP)是一个 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 快速使用
1.创建一个空的 Spring Boot 工程
2.添加依赖
2.1 引入 Spring Boot Starter 父工程
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>spring-latest-version</version>
<relativePath/>
</parent>
2.2 导入mybatis-plus-boot-starter
引入mybatis-plus在spring boot中的场景启动器
<!-- mybatis-plus 不需要再额外引入mybatis了-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatisplus.version}</version>
</dependency>
ps:切记不可再在pom.xml文件中引入mybatis与mybatis-spring的maven依赖,这一点,mybatis-plus的官方文档中已经 说明的很清楚了.
3.创建数据表
-- 创建表
CREATE TABLE tbl_employee(
id INT(11) PRIMARY KEY AUTO_INCREMENT,
last_name VARCHAR(50),
email VARCHAR(50),
gender CHAR(1),
age INT
);
INSERT INTO tbl_employee(last_name,email,gender,age) VALUES('Tom','tom@atguigu.com',1,22);
INSERT INTO tbl_employee(last_name,email,gender,age) VALUES('Jerry','jerry@atguigu.com',0,25);
INSERT INTO tbl_employee(last_name,email,gender,age) VALUES('Black','black@atguigu.com',1,30);
INSERT INTO tbl_employee(last_name,email,gender,age) VALUES('White','white@atguigu.com',0,35);
4.创建java bean
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer gender;
private Integer age;
private String genderName;
public String getGenderName() {
if(gender==0){
return "女";
}
else
{
return "男";
}
}
public Employee() {
super();
// TODO Auto-generated constructor stub
}
public Employee(Integer id, String lastName, String email, Integer gender, Integer age) {
super();
this.id = id;
this.lastName = lastName;
this.email = email;
this.gender = gender;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Employee [id=" id ", lastName=" lastName ", email=" email ", gender=" gender getGenderName() ", age="
age "]";
}
}
5.配置数据源
# 数据源
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/mybatisplus?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
#初始化时运行sql脚本
schema: classpath:sql/schema.sql
initialization-mode : never
6.创建Mapper接口
public interface EmployeeMapper extends BaseMapper<Employee> {
}
7.在SpringBoot启动类添加@MapperScan注解
@MapperScan("mapper所在包的路径")
public class QuickStartApplication {
public static void main(String[] args) {
SpringApplication.run(QuickStartApplication.class, args);
}
}
三、基于mybatis-plus的入门helloworld—CRUD实验
ps:在进行crud实验之前,简单对mybatis与mybatis-plus做一个简单的对比
1.mybatis与mybatis-plus实现方式对比
(1) 提出问题 : 假设我们已存在一张 tbl_employee 表,且已有对应的实体类 Employee,实现 tbl_employee 表的 CRUD 操作我们需要做什么呢?
(2) 实现方式 : 基于 Mybatis 需要编写 EmployeeMapper 接口,并手动编写 CRUD 方法 提供 EmployeeMapper.xml 映 射文件,并手动编写每个方法对应的 SQL 语句. 基于 Mybatis-plus 只需要创建 EmployeeMapper 接口, 并继承 BaseMapper 接口.这就是使用 mybatis-plus 需要完成的所有操作,甚至不需要创建 SQL 映射文件。
2.BaseMapper接口介绍
2.1 如何理解核心接口BaseMapper?
在使用Mybatis-Plus是,核心操作类是BaseMapper接口,其最终也是利用的Mybatis接口编程的实现机制,其默认提供 了一系列的增删改查的基础方法,并且开发人员对于这些基础操作不需要写SQL进行处理操作(Mybatis提供的机制就是 需要开发人员在mapper.xml中提供sql语句),那样我们可以猜测肯定是Mybatis-Plus完成了BaseMapper接口提供的 方法的SQL语句的生成操作。
2.2 BaseMapper接口为我们定义了哪些方法?
3.mybatis-plus中常用的注解
- @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
4.@TableName
由于我们的数据表名于实体类的类名不一致,并且实体类于数据表还存在字段名不对应的情况,因此我们需要引入mybatis- plus的注解
package com.tulingxueyuan.mbp.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
// mp 会默认将pojo类名当表名,如果类名和表名不一致可以使用注解
@TableName("tbl_employee")
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer gender;
private Integer age;
private String genderName;
public String getGenderName() {
if(gender==0){
return "女";
}
else
{
return "男";
}
}
public Employee() {
super();
// TODO Auto-generated constructor stub
}
public Employee(Integer id, String lastName, String email, Integer gender, Integer age) {
super();
this.id = id;
this.lastName = lastName;
this.email = email;
this.gender = gender;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Employee [id=" id ", lastName=" lastName ", email=" email ", gender=" gender getGenderName() ", age="
age "]";
}
}
5.增删查改操作
5.1 编写EmployeeMapper接口继承BaseMapper接口
public interface EmployeeMapper extends BaseMapper<Employee> {
}
5.2 准备测试环境
@SpringBootTest
class QuickStartApplicationTests {
@Autowired
EmployeeMapper employeeMapper;
}
5.3 查询
- selectById方法
@Test
void query() {
System.out.println(employeeMapper.selectById(1));
}
- selectBatchIds方法
@Test
void queryBatchIds(){
List<Integer> ids = new ArrayList<>();
ids.add(1);
ids.add(2);
ids.add(3);
List<Employee> employees = employeeMapper.selectBatchIds(ids);
System.out.println(employees);
}
ps:发现该方法底层使用的竟然是sql的in关键字
- selectByMap方法
@Test
void queryByMap(){
Map<String,Object> map = new HashMap<String,Object>();
map.put("age",22);
map.put("id",1);
List<Employee> employees = employeeMapper.selectByMap(map);
System.out.println(employees);
}
5.4 增加
@Test
void add() {
Employee zhang = new Employee(null, "zhang", "123@qq.com", 1, 18);
employeeMapper.insert(zhang);
// 插入后可以立即得到主键
System.out.println(zhang);
}
- 注意加注解主键生成策略
// mp 会默认将pojo类名当表名,如果类名和表名不一致可以使用注解
@TableName("tbl_employee")
public class Employee {
// mp 会自动识别pojo类中名为id的属性,如果名字叫id就会当做是主键
// 如果你的注解没有赋值那它会帮你使用ID_WORKER的生成策略, 主要是为了防止你忘记给主键赋值
// 如果字段是自动增长需要手动改一下生成策略
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@TableField("last_name")
private String lastName;
private String email;
private Integer gender;
private Integer age;
@TableField(exist = false)
private String genderName;
public String getGenderName() {
if(gender==0){
return "女";
}
else
{
return "男";
}
}
public Employee() {
super();
// TODO Auto-generated constructor stub
}
public Employee(Integer id, String lastName, String email, Integer gender, Integer age) {
super();
this.id = id;
this.lastName = lastName;
this.email = email;
this.gender = gender;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Employee [id=" id ", lastName=" lastName ", email=" email ", gender=" gender getGenderName() ", age="
age "]";
}
}
5.5 修改
@Test
void update() {
Employee zhang = new Employee(6, "biao", "123@qq.com", 1, 18);
employeeMapper.updateById(zhang);
// 插入后可以立即得到主键
System.out.println(zhang);
}
5.6 删除
- // 删除(根据ID 批量删除)
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); - // 根据 ID 删除
int deleteById(Serializable id); - // 根据 columnMap 条件,删除记录
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
@Test
void delete() {
employeeMapper.deleteById(5);
}
四、Service层的CRUD
1.接口继承IService
public interface EmployeeService extends IService<Employee> {
}
2.接口实现类继承基类ServiceImpl,指定其Mapper和pojo类作为泛型
service实现类 继承mp提供通用的service基类,ServiceImpl<EmployeeMapper, Employee>
2个泛型
- 1.EmployeeMapper Mapper接口
- 2.Employee 对应Pojo
@Service
public class EmployeeImplService extends ServiceImpl<EmployeeMapper, Employee> implements EmployeeService {
}
3.CRUD命令
CRUD命令语句可以去官网中查询https://mp.baomidou.com/guide/crud-interface.html#service-crud-接口
4.分页查询
4.1 分页插件
- 配置SSM项目。
<!-- spring xml 方式 -->
<property name="plugins">
<array>
<bean class="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor">
<property name="sqlParser" ref="自定义解析类、可以没有"/>
<property name="dialectClazz" value="自定义方言类、可以没有"/>
<!-- COUNT SQL 解析.可以没有 -->
<property name="countSqlParser" ref="countSqlParser"/>
</bean>
</array>
</property>
<bean id="countSqlParser" class="com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize">
<!-- 设置为 true 可以优化部分 left join 的sql -->
<property name="optimizeJoin" value="true"/>
</bean>
- SpringBoot方式
放下列代码进启动类中
// 最新版
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
return interceptor;
}
4.2 在XML中运用分页插件
- 配置Mapper.xml
<?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.tulingxueyuan.mbp.mapper.EmployeeMapper">
<select id="getByGender" resultType="com.tulingxueyuan.mbp.pojo.Employee">
SELECT * FROM tbl_employee where gender=#{gender}
</select>
</mapper>
- Mapper接口中定义
public interface EmployeeMapper extends BaseMapper<Employee> {
IPage<Employee> getByGender(IPage page,Integer gender);
}
4.3 SpringBoot配置文件中配置
pagehelper:
helper-dialect: mysql
reasonable: true
4.4 测试
@Test
void xmlPage(){
IPage<Employee> iPage=new Page<>(1,1);
System.out.println(mapper.getByGender(iPage, 1).getRecords());
}
4.5 注意
mp 默认的mapper.xml路径地址是classpath*:/mapper/**/*.xml ,如果不是需要通过以下配置设置
五、Wapper-条件构造器
1.wrapper及其子类介绍
-
Wrapper :条件构造抽象类,最顶端父类,抽象类中提供3个方法以及其他方法.
-
AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件,QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件
AbstractWrapper比较重要,里面的方法需要重点学习
- AbstractLambdaWrapper : Lambda 语法使用 Wrapper统一处理解析 lambda 获取 column。
- LambdaQueryWrapper :看名称也能明白就是用于Lambda语法使用的查询Wrapper
- LambdaUpdateWrapper : Lambda 更新封装Wrapper
- QueryWrapper : Entity 对象封装操作类,不是用lambda语法,自身的内部属性 entity 也用于生成 where 条件 该类的重要方法
select方法
select(String... sqlSelect)
select(Predicate<TableFieldInfo> predicate)
select(Class<T> entityClass, Predicate<TableFieldInfo> predicate)
/*例: select("id", "name", "age")
例: select(i ‐> i.getProperty().startsWith("test"))
*/
- UpdateWrapper : Update 条件封装,用于Entity对象更新操作
该类主要有以下三个重要的方法:
- set
set(String column, Object val)
set(boolean condition, String column, Object val)
例: set(“name”, “老李头”)
例: set(“name”, “”)—>数据库字段值变为空字符串
例: set(“name”, null)—>数据库字段值变为nul
- SQL SET 字段
setSql(String sql)
设置 SET 部分 SQL
例: setSql(“name = ‘老李头’”)
2.带条件的crud实验
// 根据 entity 条件,查询一条记录
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 entity 条件,查询全部记录
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 entity 条件,查询全部记录(并翻页)
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录(并翻页)
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWr apper);
// 根据 Wrapper 条件,查询总记录数
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
3.带条件的更新
@Test
void update() {
UpdateWrapper<Employee> updateWrapper=new UpdateWrapper<Employee>();
updateWrapper.eq("last_name", "lili").eq("age", 18).set("id", 100).set(false, "email", "000@qq.com"); empolyeeMapper.update(employee, updateWrapper);
}
}
其中set(“id”, 100).set(false, “email”, “000@qq.com”);中email属性设置为false,从执行的sql可以看出,设置为false不会 拼接到最终的执行sql中
4.带条件的删除
// 根据 entity 条件,删除记录
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
// 根据 columnMap 条件,删除记录
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
六、拓展
1.全局ID生成策略
在全局配置文件中: 就不需要再每个Pojo主键上配置了
mybatis‐plus:
global‐config:
db‐config:
id‐type: auto
2.逻辑删除
- 物理删除: 在删除的时候直接将数据从数据库干掉DELTE
- 逻辑删除: 从逻辑层面控制删除,通常会在表里添加一个逻辑删除的字段比如 enabled 、is_delete ,数据默认是有效的(值为1), 当用户删除时将数据修改UPDATE 0, 在查询的时候就只查where enabled=1. 1. 需要添加逻辑删除的字段 2. 局部单表逻辑删除,需要在对应的pojo类加入对应的逻辑删除标识字段
- 需要添加逻辑删除的字段
- 局部单表逻辑删除,需要在对应的pojo类加入对应的逻辑删除标识字段
@TableLogic // 代表逻辑删除
private Integer flag;
public Integer getFlag() {
return flag;
}
全局逻辑删除配置, 如果进行了全局逻辑删除配置并且指定了,就可以不用在每个pojo类中配置了@TableLogic
mybatis‐plus:
global‐config:
db‐config:
logic‐delete‐field: flag
# 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic‐delete‐value: 1 # 逻辑已删除值(默认为 1)
logic‐not‐delete‐value: 0 # 逻辑未删除值(默认为 0)
id-type:auto
3.自动填充功能
原理:
- 实现元对象处理器接口:com.baomidou.mybatisplus.core.handlers.MetaObjectHandler
- 注解填充字段 @TableField(… fill = FieldFill.INSERT) 生成器策略部分也可以配置!
步骤一:pojo类加上注解
// 创建时间:希望在添加数据的时候填充:当前时间
@TableField(fill = FieldFill.INSERT)
private Date createDate;
// 修改时间:希望在添加数据、修改数据的时候填充:当前时间
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date modifyDate;
步骤二
自定义实现类 MyMetaObjectHandler
@Slf4j
@Component
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
// 插入时:创建时间和修改时间
this.setFieldValByName("createDate",new Date(),metaObject);
this.setFieldValByName("modifyDate",new Date(),metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("modifyDate",new Date(),metaObject);
}
}
七、执行SQL分析打印
1.p6spy 依赖引入
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>最新版本</version>
</dependency>
2.application.yml 配置
spring:
datasource:
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:h2:mem:test
3.创建spy.properties 配置文件
#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
4.注意
- driver-class-name 为 p6spy 提供的驱动类
- url 前缀为 jdbc:p6spy 跟着冒号为对应数据库连接地址
- 打印出sql为null,在excludecategories增加commit
- 批量操作不打印sql,去除excludecategories中的batch
- 批量操作打印重复的问题请使用MybatisPlusLogFactory (3.2.1新增)
- 该插件有性能损耗,不建议生产环境使用。
八、数据安全保护
防止删库跑路
1.得到16位随机秘钥
@Test
void test(){// 生成 16位随机 AES 密钥
String randomKey = AES.generateRandomKey(); System.out.println(randomKey);
}
da12166c7db8a58f
2.根据秘钥加密 数据库连接信息
@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);
}
3.修改配置文件 注意要mpw:开头
username: mpw:0Cj49ihj1Q6UbkRfixFdVg==
password: mpw:yp192XvO1C0jq67MeCvlIg==
url: mpw:nIh0E63gBfvpFbz2tXDyWDN2kFpD apc9JaRYosGY5sKL3zyNwalK3OfGo27p8AM8BL0llHGFwpfdELaf79NIxm8kfOMhUdOFLNy7g85BTCrEzbYEHqp3THf7KOz80Ka
4.在部署的时候需要解密
java ‐jar xxxx.jar ‐‐mpw.key=你的16位随机秘钥, 越少人知道越好
九、乐观锁插件使用
1.什么是乐观锁
- 悲观锁:悲观锁,正如其名,具有强烈的独占和排他特性。它指的是对数据被外界(包括本系统当前的其他事 务,以及来自外部系统的事务处理)修改持保守态度。因此,在整个数据处理过程中,将数据处于锁定状态。
假设功能并发量非常大,就需要使用synchronized来处理高并发下产生线程不安全问题, 会使其他线程进行挂起等待从 而影响系统吞吐量
- 乐观锁:乐观锁是相对悲观锁而言的,乐观锁假设数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正 式对数据的冲突与否进行检测,如果发现冲突了,则返回给用户错误的信息,让用户决定如何去做。乐观锁适用于读操作多的场 景,这样可以提高程序的吞吐量。
假设功能产生并发几率极少,采用乐观锁版本机制对比, 如果有冲突 返回给用户错 误的信息
2.为什么需要锁(并发控制)
在多用户环境中,在同一时间可能会有多个用户更新相同的记录,这会产生冲突。这就是著名的并发性问题
- 丢失更新:一个事务的更新覆盖了其它事务的更新结果,就是所谓的更新丢失。例如:用户1把值从500改为 8000,用户B把值从500改为200,则多人同时提交同一条记录,后提交的把之前的提交数据覆盖。
- 脏读:当一个事务读取其它完成一半事务的记录时,就会发生脏读。例如:用户A,B看到的值都是500,用户B 把值改为200,用户A读到的值仍为500。
针对一种问题的解决方案,为解决问题而生的。解决什么问题呢?主要是解决丢失更新问题如下图理解
为了解决这些并发带来的问题。 我们需要引入并发控制机制
3.乐观锁使用MyBatisPlus的解决方式
由于锁这个字眼我们需要在数据库加个字段“version”来控制版本
3.1 在类中加个属性
@Version //这就是控制版本的
@TableField(fill = FieldFill.INSERT) //这个方便在添加的时候设置版本初始为1
private Integer version; //版本的字段
3.2 下面这个也是MyBatisPlus的一个插件 只需要实现MetaObjectHandler就可以了
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) { 5 //这里的“version”就是指定的字段,设置初始值为1,之后每修改一次 1
this.setFieldValByName("version",1,metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
}
}
3.3 在MyBatis中存在一个乐观锁插件: OptimisticLockerInnerInterceptor
@Configuration
@MapperScan("com.lzz.mapper")
public class MyConfig {
//乐观锁插件
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor(){
return new OptimisticLockerInterceptor();
}
}
十、代码生成器
package com.tulingxueyuan;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.LikeTable;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class GeneratorApp {
/**
* <p>
* 读取控制台内容
* </p>
*/
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
StringBuilder help = new StringBuilder();
help.append("请输入" tip ":");
System.out.println(help.toString());
// 判断用户是否输入
if (scanner.hasNext()) {
// 拿到输入内容
String ipt = scanner.next();
if (StringUtils.isNotBlank(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" tip "!");
}
public static void main(String[] args) {
String moduleName = scanner("模块名");
String tableName = scanner("表名(多个用,号分隔,或者按前缀(pms*))");
String prefixName = scanner("需要替换的表前缀");
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
// 获得当前项目的路径
String projectPath = System.getProperty("user.dir") "/05_generator";
// 设置生成路径
gc.setOutputDir(projectPath "/src/main/java");
// 作者
gc.setAuthor("xushu");
// 代码生成是不是要打开所在文件夹
gc.setOpen(false);
// 生成Swagger2注解
gc.setSwagger2(true);
// 会在mapper.xml 生成一个基础的<ResultMap> 映射所有的字段
gc.setBaseResultMap(true);
// 同文件生成覆盖
gc.setFileOverride(true);
//gc.setDateType(DateType.ONLY_DATE)
// 实体名:直接用表名 %s=表名
gc.setEntityName("%s");
// mapper接口名
gc.setMapperName("%sMapper");
// mapper.xml 文件名
gc.setXmlName("%sMapper");
// 业务逻辑类接口名
gc.setServiceName("%sService");
// 业务逻辑类实现类名
gc.setServiceName("%sImplService");
// 将全局配置设置到AutoGenerator
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mall?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
// 模块名
pc.setModuleName(moduleName);
// 包名
pc.setParent("com.biao");
// 完整的报名: com.biao.pms
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
// 如果模板引擎是 velocity
String templatePath = "/templates/mapper.xml.vm";
// 自定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
// 自定义配置会被优先输出
focList.add(new FileOutConfig(templatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
return projectPath "/src/main/resources/mapper/" pc.getModuleName()
"/" tableInfo.getEntityName() "Mapper" StringPool.DOT_XML;
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 配置模板
TemplateConfig templateConfig = new TemplateConfig();
// 把已有的xml生成置空
templateConfig.setXml(null);
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
// 表名的生成策略:下划线转驼峰 pms_product -- PmsProduct
strategy.setNaming(NamingStrategy.underline_to_camel);
// 列名的生成策略:下划线转驼峰 last_name -- lastName
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
//strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
strategy.setEntityLombokModel(true);
// 在controller类上是否生成@RestController
strategy.setRestControllerStyle(true);
// 公共父类
//strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!");
if(tableName.indexOf('*')>0){
// 按前缀生成表
strategy.setLikeTable(new LikeTable(tableName.replace('*','_')));
}
else{
// 要生成的表名 多个用逗号分隔
strategy.setInclude(tableName);
}
// 设置表替换前缀
strategy.setTablePrefix(prefixName);
// 驼峰转连字符 比如 pms_product --> controller @RequestMapping("/pms/pmsProduct")
//strategy.setControllerMappingHyphenStyle(true);
mpg.setStrategy(strategy);
// 进行生成
mpg.execute();
}
}
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgakbjj
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01