-
ActiveRecord
-
Mybatis-Plus的插件
-
Sql 注入器实现自定义全局操作
-
自动填充功能
-
逻辑删除
-
通用枚举
-
代码生成器
-
MybatisX 快速开发插件
1、ActiveRecord
ActiveRecord(简称AR)一直广受动态语言( PHP 、 Ruby 等)的喜爱,而 Java 作为准静态语言,对于 ActiveRecord 往往只能感叹其优雅,所以我们也在 AR 道路上进行了一定的探索,喜欢大家能够喜欢。
什么是ActiveRecord?
ActiveRecord也属于ORM(对象关系映射)层,由Rails最早提出,遵循标准的ORM模型:表映射到记录,记 录映射到对象,字段映射到对象属性。配合遵循的命名和配置惯例,能够很大程度的快速实现模型的操作,而 且简洁易懂。
ActiveRecord的主要思想是:
每一个数据库表对应创建一个类,类的每一个对象实例对应于数据库中表的一行记录;通常表的每个字段 在类中都有相应的Field;
ActiveRecord同时负责把自己持久化,在ActiveRecord中封装了对数据库的访问,即CURD;;
ActiveRecord是一种领域模型(Domain Model),封装了部分业务逻辑;
1.1、开启AR之旅
在MP中,开启AR非常简单,只需要将实体对象继承Model即可。
1.2、根据主键查询
1.3、新增数据
1.5、更新操作
1.6、删除操作
1.7、根据条件查询
3、插件
3.1、mybatis的插件机制
MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法 调用包括:
-
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
-
ParameterHandler (getParameterObject, setParameters)
-
ResultSetHandler (handleResultSets, handleOutputParameters)
-
StatementHandler (prepare, parameterize, batch, update, query)
我们看到了可以拦截Executor接口的部分方法,比如update,query,commit,rollback等方法,还有其他接口的 一些方法等。
总体概括为:
-
拦截执行器的方法
-
拦截参数的处理
-
拦截结果集的处理
-
拦截Sql语法构建的处理
拦截器示例:
注入到Spring容器:
/**
* 自定义拦截器
*/
或者通过xml配置,mybatis-config.xml:
3.2、执行分析插件
在MP中提供了对SQL执行的分析的插件,可用作阻断全表更新、删除的操作,注意:该插件仅适用于开发环境,不 适用于生产环境。
SpringBoot配置:
测试:
可以看到,当执行全表更新时,会抛出异常,这样有效防止了一些误操作。
3.3、性能分析插件
性能分析拦截器,用于输出每条 SQL 语句及其执行时间,可以设置最大执行时间,超过时间会抛出异常。
该插件只用于开发环境,不建议生产环境使用。
配置:
Time:11 ms - ID:cn.itcast.mp.mapper.UserMapper.selectById
Execute SQL:
SELECT
id,
user_name,
password,
name,
age,
email
FROM
tb_user
WHERE
id=7
可以看到,执行时间为11ms。如果将maxTime设置为1,那么,该操作会抛出异常。
Caused by: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: The SQL
execution time is too large, please optimize !
at com.baomidou.mybatisplus.core.toolkit.ExceptionUtils.mpe(ExceptionUtils.java:49)
at com.baomidou.mybatisplus.core.toolkit.Assert.isTrue(Assert.java:38)
................
3.4、乐观锁插件
3.4.1、主要适用场景
意图: 当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:
-
取出记录时,获取当前version
-
更新时,带上这个version
-
执行更新时, set version = newVersion where version = oldVersion
-
如果version不对,就更新失败
3.4.2、插件配置
spring xml:
<bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"/>
spring boot:
3.4.3、注解实体字段
需要为实体字段添加@Version注解。
第一步,为表添加version字段,并且设置初始值为1:
ALTER TABLE `tb_user`
ADD COLUMN `version` int(10) NULL AFTER `email`;
UPDATE `tb_user` SET `version`='1';
第二步,为User实体对象添加version字段,并且添加@Version注解:
3.4.4、测试
测试用例:
执行日志:
main] [com.baomidou.mybatisplus.extension.parsers.BlockAttackSqlParser]-[DEBUG]
Original SQL: UPDATE tb_user SET age=?,
version=? WHERE id=? AND version=?
[main] [com.baomidou.mybatisplus.extension.parsers.BlockAttackSqlParser]-[DEBUG]
parser sql: UPDATE tb_user SET age = ?, version = ? WHERE id = ? AND version = ?
[main] [org.springframework.jdbc.datasource.DataSourceUtils]-[DEBUG] Fetching JDBC
Connection from DataSource
[main] [org.mybatis.spring.transaction.SpringManagedTransaction]-[DEBUG] JDBC
Connection [HikariProxyConnection
可以看到,更新的条件中有version条件,并且更新的version为2。
如果再次执行,更新则不成功。这样就避免了多人同时更新时导致数据的不一致。
3.4.5、特别说明
-
支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
-
整数类型下 newVersion = oldVersion + 1
-
newVersion 会回写到 entity 中
-
仅支持 updateById(id) 与 update(entity, wrapper) 方法
-
在 update(entity, wrapper) 方法下, wrapper 不能复用!!!
4、Sql 注入器
我们已经知道,在MP中,通过AbstractSqlInjector将BaseMapper中的方法注入到了Mybatis容器,这样这些方法才 可以正常执行。
那么,如果我们需要扩充BaseMapper中的方法,又该如何实现呢? 下面我们以扩展findAll方法为例进行学习。
4.1、编写MyBaseMapper
package cn.itcast.mp.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.util.List;
public interface MyBaseMapper<T> extends BaseMapper<T> {
List<T> findAll();
}
其他的Mapper都可以继承该Mapper,这样实现了统一的扩展。
如:
package cn.itcast.mp.mapper;
import cn.itcast.mp.pojo.User;
public interface UserMapper extends MyBaseMapper<User> {
User findById(Long id);
}
4.2、编写MySqlInjector
如果直接继承AbstractSqlInjector的话,原有的BaseMapper中的方法将失效,所以我们选择继承DefaultSqlInjector 进行扩展。
package cn.itcast.mp.sqlInjector;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import java.util.List;
public class MySqlInjector extends DefaultSqlInjector {
4.3、编写FindAll
package cn.itcast.mp.sqlInjector;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
public class FindAll extends AbstractMethod {
4.4、注册到Spring容器
/**
* 自定义SQL注入器
*/
4.5、测试
输出的SQL:
[main] [cn.itcast.mp.mapper.UserMapper.findAll]-[DEBUG] ==> Preparing: select * from
tb_user
[main] [cn.itcast.mp.mapper.UserMapper.findAll]-[DEBUG] ==> Parameters:
[main] [cn.itcast.mp.mapper.UserMapper.findAll]-[DEBUG] <== Total: 10
至此,我们实现了全局扩展SQL注入器。
5、自动填充功能
有些时候我们可能会有这样的需求,插入或者更新数据时,希望有些字段可以自动填充数据,比如密码、version 等。在MP中提供了这样的功能,可以实现自动填充。
5.1、添加@TableField注解
为password添加自动填充功能,在新增数据时有效。 FieldFill提供了多种模式选择:
public enum FieldFill {
/**
* 默认不处理
*/
DEFAULT,
/**
* 插入时填充字段
*/
INSERT,
/**
* 更新时填充字段
*/
UPDATE,
/**
* 插入和更新时填充字段
*/
INSERT_UPDATE
}
5.2、编写MyMetaObjectHandler
package cn.itcast.mp.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
5.3、测试
6、 逻辑删除
开发系统时,有时候在实现功能时,删除操作需要实现逻辑删除,所谓逻辑删除就是将数据标记为删除,而并非真正 的物理删除(非DELETE操作),查询时需要携带状态条件,确保被标记的数据不被查询到。这样做的目的就是避免 数据被真正的删除。
MP就提供了这样的功能,方便我们使用,接下来我们一起学习下。
6.1、修改表结构
为tb_user表增加deleted字段,用于表示数据是否被删除,1代表删除,0代表未删除。
ALTER TABLE `tb_user`
ADD COLUMN `deleted` int(1) NULL DEFAULT 0 COMMENT '1代表删除,0代表未删除' AFTER
`version`;
同时,也修改User实体,增加deleted属性并且添加@TableLogic注解:
6.2、配置
application.properties:
# 逻辑已删除值(默认为 1)
mybatis-plus.global-config.db-config.logic-delete-value=1
# 逻辑未删除值(默认为 0)
mybatis-plus.global-config.db-config.logic-not-delete-value=0
6.3、测试
执行的SQL:
[main] [cn.itcast.mp.mapper.UserMapper.deleteById]-[DEBUG] ==> Preparing: UPDATE
tb_user SET deleted=1 WHERE id=? AND deleted=0
[main] [cn.itcast.mp.mapper.UserMapper.deleteById]-[DEBUG] ==> Parameters: 2(Long)
[main] [cn.itcast.mp.mapper.UserMapper.deleteById]-[DEBUG] <== Updates: 1
测试查询:
执行的SQL:
[main] [cn.itcast.mp.mapper.UserMapper.selectById]-[DEBUG] ==> Preparing: SELECT
id,user_name,password,name,age,email,version,deleted FROM tb_user WHERE id=? AND
deleted=0
[main] [cn.itcast.mp.mapper.UserMapper.selectById]-[DEBUG] ==> Parameters: 2(Long)
[main] [cn.itcast.mp.mapper.UserMapper.selectById]-[DEBUG] <== Total: 0
可见,已经实现了逻辑删除。
7、 通用枚举
解决了繁琐的配置,让 mybatis 优雅的使用枚举属性!
7.1、修改表结构
ALTER TABLE `tb_user`
ADD COLUMN `sex` int(1) NULL DEFAULT 1 COMMENT '1-男,2-女' AFTER `deleted`;
7.2、定义枚举
package cn.itcast.mp.enums;
import com.baomidou.mybatisplus.core.enums.IEnum;
import com.fasterxml.jackson.annotation.JsonValue;
public enum SexEnum implements IEnum<Integer> {
MAN(1,"男"),
WOMAN(2,"女");
private int value;
private String desc;
SexEnum(int value, String desc) {
this.value = value;
this.desc = desc;
}
7.3、配置
# 枚举包扫描
mybatis-plus.type-enums-package=cn.itcast.mp.enums
7.4、修改实体
private SexEnum sex;
7.5、测试
测试插入数据:
SQL:
[main] [cn.itcast.mp.mapper.UserMapper.insert]-[DEBUG] ==> Preparing: INSERT INTO
tb_user ( user_name, password, name, age, email, version, sex ) VALUES ( ?, ?, ?, ?, ?,
?, ? )
[main] [cn.itcast.mp.mapper.UserMapper.insert]-[DEBUG] ==> Parameters:
diaochan(String), 123456(String), 貂蝉(String), 20(Integer), diaochan
查询:
结果:
[main] [cn.itcast.mp.mapper.UserMapper.selectById]-[DEBUG] ==> Preparing: SELECT
id,user_name,password,name,age,email,version,deleted,sex FROM tb_user WHERE id=? AND
deleted=0
[main] [cn.itcast.mp.mapper.UserMapper.selectById]-[DEBUG] ==> Parameters: 2(Long)
[main] [cn.itcast.mp.mapper.UserMapper.selectById]-[DEBUG] <== Total: 1
User(id=2, userName=lisi, password=123456, name=李四, age=30, email=test2
从测试可以看出,可以很方便的使用枚举了。
查询条件时也是有效的:
SQL:
[main] [cn.itcast.mp.mapper.UserMapper.selectList]-[DEBUG] ==> Preparing: SELECT
id,user_name,password,name,age,email,version,deleted,sex FROM tb_user WHERE deleted=0
AND sex = ?
[main] [cn.itcast.mp.mapper.UserMapper.selectList]-[DEBUG] ==> Parameters: 2(Integer)
[main] [cn.itcast.mp.mapper.UserMapper.selectList]-[DEBUG] <== Total: 3
8、代码生成器
AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。
8.1、创建工程
pom.xml: