ORM框架
- 一:Mybatis-plus 3.4版本
- 1:概念
- 2:框架结构
- 3:注解
- 4:排除实体中非表字段
- 5:条件构造器
- 6:Active Record 模式
- 7:策略
- 8:通用Service
- 二:Mybatis-plus 高级功能
- 1:逻辑删除
- 2:自动填充
- 3:乐观锁插件
- 4:性能分析插件
- 5:多租户SQL解析器
- 6:动态表名SQL解析器
- 7:SQL注入器
- 3:传统ssm编程结构
一:Mybatis-plus 3.4版本
1:概念
1.1:11 — 特性
– 01 无侵入
:只做增强不做改变,引入它不会对现有工程产生影响
– 02 损耗小
启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
– 03 内置 CRUD 操作
内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
– 04 条件构造器
普通条件构造器
lambda表达式条件构造器
lambda表达式链条件构造器
– 05 多种主键策略
UUID策略
默认策略雪花算法,插入当实体时如果主键没有手动设置,该策略会自动填充主键字段
Auto策略 依赖数据库的主键自增机制,当插入一个实体时,被插入的实体主键将会回显。
– 06 内置插件
内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询,分页插件支持多种数据库: MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
– 07 支持自定义全局通用操作
支持全局通用方法注入( Write once, use anywhere )
– 08 支持 ActiveRecord 模式
支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
– 09 内置代码生成器
采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎
1.2:03 — mybatis 优势
– 01 sql可以自由控制 更灵活 性能较jpa更高
– 02 sql 与代码分离 易于阅读 和 维护
– 03 支持xml标签 支持编写动态sql语句
1.3:03 — mybatis 劣势
– 01 简单CRUD需编写sql语句
– 02 xml中有大量的sql需要维护
– 03 mybatis 自身功能有限 但支持 pugins
2:框架结构
2.1:04 — mybatis-plus 结构
– 01 注解
– 02 扩展
– 03 核心
– 04 生成器
2.2:00 — 框架目标
– 04 目标思路
扫描所有的实体类
反射提取
分析表字段
inser,updatte,delete,select 注入 mybatis 容器
3:注解
3.1:03 — 常用注解
– 01 @TableName
指定实体对应数据库的表名
– 02 @TableId
指定实体中对应表主键字段
– 03 @TableField(“表字段”)
指定实体对应数据库表的字段
4:排除实体中非表字段
4.1:00 — transient 关键字,
– private transient String temp; 该字段不会被序列化以及反序列化
4.2:00 — static 关键字
– private static String temp; 静态字段不会解析成表字段
4.3:00 — @TableField(exist = false) 注解
– 非表字段
5:条件构造器
分为普通条件构造器,lambda表达式条件构造器,lambda表达式链条件构造器
5.1:00 — 条件查询
– queryWrapper.apply(“date_format(日期字段, ‘%Y-%m-%d’) ={0}”,“2020-04-27”) 查找指定日期 条件
– date_format(日期, ‘%Y-%m-%d’) - > 格式化指定日期
5.2:00 — 查询指定的列
– queryWrapper.select(列名1…)
5.3:00 — condition 的作用
– 控制该条件是否加入到查询语句中,常用于用户查询
5.4:00 — 带实体参数的条件构造器
– 以下whereUser 实体中不为null值的字段会被作 等值 查询条件拼接在 sql语句中
5.5:00 — queryWrapper.allEq(map) 的用法
– queryWrapper.allEq(map, true):map会被解析成 等值查询, 字段值是null时会被解析成 :字段 is null
– queryWrapper.allEq(map, false):字段值是null时 不会被加入到 查询条件
5.6:00 — lambda 条件构建器
– 01 创建lambda 条件构建器
5.7:00 — 使用条件构造器的自定义sql
–
5.8:00 — 分页查询
配置mybatis-plus分页插件
new Page<>(3, 1, false); 不需要执行 count统计sql,只返回记录
– Mapper.selectMapsPage
– Mapper.selectPage
5.9:00 — 普通 update 更新方法
– 实体中非null字段将被构造在sql的 set 语句中
public void update(){
UpdateWrapper<UserTest> updateWrapper = new UpdateWrapper<UserTest>();
updateWrapper.eq("user_name", "张三");
UserTest userTest = new UserTest();
userTest.setPwd("1111111");
userTest.setRemaker("11111111");
Integer jl = iUserTestMapper.update(userTest, updateWrapper);
System.out.println(String.format("记录数:%s",jl));
}
– 不需要实体更新单个字段值
public void update(){
UserTest userTest = new UserTest();
userTest.setId(1);
// 当只要更新单个字段时 可以设置 update 更新对象为null, 并在更新构造器中set需要更改的字段以及值
UpdateWrapper<UserTest> updateWrapper = new UpdateWrapper<UserTest>(userTest).set("pwd", "123456");
Integer jl = iUserTestMapper.update(null, updateWrapper);
System.out.println(String.format("记录数:%s",jl));
}
5.10:00 — lambda方式 update 更新方法
– LambdaUpdateWrapper方式 更新单个字段
public void update(){
LambdaUpdateWrapper<UserTest> objectLambdaUpdateWrapper = Wrappers.lambdaUpdate();
objectLambdaUpdateWrapper.eq(UserTest::getId, "1").set(UserTest::getRemaker, "LambdaUpdateWrapper");
Integer jl = iUserTestMapper.update(null, objectLambdaUpdateWrapper);
System.out.println(String.format("记录数:%s",jl));
}
– LambdaUpdateChainWrapper方式 更新单个字段
public void update(){
LambdaUpdateChainWrapper<UserTest> userTestLambdaUpdateChainWrapper = new LambdaUpdateChainWrapper<>(iUserTestMapper);
boolean result = userTestLambdaUpdateChainWrapper.eq(UserTest::getId,1).set(UserTest::getRemaker, "LambdaUpdateChainWrapper").update();
System.out.println(String.format("更新结果:%s",result));
}
5.11:00 — 删除语句
– map条件删除
public void delete(){
Map<String, Object> whereMap = new HashMap<>();
whereMap.put("id", 3);
Integer result = iUserTestMapper.deleteByMap(whereMap);
System.out.println(String.format("删除结果:%s", result));
}
– 查询条件构造器 删除
public void delete(){
LambdaQueryWrapper<UserTest> objectLambdaQueryWrapper = Wrappers.<UserTest>lambdaQuery();
objectLambdaQueryWrapper.eq(UserTest::getId, 4);
Integer result = iUserTestMapper.delete(objectLambdaQueryWrapper);
System.out.println(String.format("删除结果:%s", result));
}
6:Active Record 模式
6.1:00 — 概念
– 通过模型实体直接对表进行CRUD操作
6.2:00 — MP实现AR模式需要两个基本条件
– public class UserTest extends Model
– 必须存在原始的Mapper接口 并存在BaseMapper
– 使用 实体类进行CRUD操作
7:策略
7.1:00 — 局部主键策略实现
– 主键依赖 数据库 自增策略 @TableId(type = IdType.AUTO);使用该策略 新增数据后 ID会回显
– 默认策略 @TableId(type = IdType.NONE):MP默认策略是雪花算法,当主键ID为Null会自动添加主键值
7.2:00 — 全局主键策略实现
– 在配置文件中设置 主键 全局策略
7.3:00 — 全局策略实现
– 在该配置中可以设置 mybatis 许多默认行为
8:通用Service
public interface IUserTestService extends IService<UserTest> {
}
@Service
public class UserTestServiceImpl extends ServiceImpl<IUserTestMapper, UserTest> implements IUserTestService {
}
8.1:00 — 基本方法
–
8.2:00 — 批量操作方法
–
public void selectFromService(){
UserTest userTest1 = new UserTest();
UserTest userTest2 = new UserTest();
userTest1.setId(4);
userTest1.setUserName("张三1");
userTest1.setPwd("1111111");
userTest2.setId(5);
userTest2.setUserName("李四");
userTest2.setPwd("22222222");
boolean saveBatch = iUserTestService.saveBatch(Arrays.asList(userTest1, userTest2));
System.out.println(saveBatch);
}
8.3:00 — 链式调用方法
–
public void selectFromService(){
List<UserTest> userTests = iUserTestService.lambdaQuery().likeRight(UserTest::getUserName, "a").list();
userTests.forEach(System.out::println);
}
public void selectFromService(){
boolean result = iUserTestService.lambdaUpdate().likeRight(UserTest::getUserName, "kernel").remove();
System.out.println(String.format("删除结果:%s", result));
}
二:Mybatis-plus 高级功能
1:逻辑删除
1.1:03 — 逻辑删除 全局配置
– 逻辑删除 全局配置
– 建立配置类 ,3.1.1之后的高版本不用配置插件
public ISqlInjector iSqlInjector(){
return new LogicSqlInjector();
}
– 标识逻辑删除字段
@TableLogic
private Integer deleted;
1.2:00 — 排除逻辑删除字段
– 查询的时候不查询该字段
@TableLogic
@TableField(select = false)
private Integer deleted;
1.3:00 — 自定义的mapper 方法不会自动加逻辑删除标识
– 需要 添加 请在 条件构造器中 手动添加 如:queryMapper.eq(“deleted”, 0);
2:自动填充
2.1:02 — MP实现自动填充
– 标识操作类型 填充
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.UPDATE)
private LocalDateTime updateTime;
– 建立填充处理器
@Component
public class MyBatisMetaObjHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
setFieldValByName("createTime", LocalDateTime.now(),metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
setFieldValByName("updateTime", LocalDateTime.now(),metaObject);
}
}
2.2:00 — 注意事项
– 当使用lambda update构造器 set方法 更新单个字段时 更新时间字段不会自动填充
//以下方法不会自动填充 更新时间字段
userTestLambdaUpdateWrapper.eq(UserTest::getId, 1).set(UserTest::getUserName, "张三");
iUserTestMapper.update(null, userTestLambdaUpdateWrapper);
或者
userTestLambdaUpdateChainWrapper.eq(UserTest::getId,1).set(UserTest::getRemaker, "10000").update();
2.3:00 — 自动填充优化
– 检查是否存在 需要填充的字段,以及检查是否手动填充了该字段
public void insertFill(MetaObject metaObject) {
if (metaObject.hasSetter("createTime")){
setFieldValByName("createTime", LocalDateTime.now(),metaObject);
}
}
// 检查是否手动填充了该字段
public void updateFill(MetaObject metaObject) {
if (metaObject.hasSetter("updateTime") && getFieldValByName("updateTime", metaObject) == null){
System.out.println("更新 填充值");
setFieldValByName("updateTime", LocalDateTime.now(),metaObject);
}
}
3:乐观锁插件
3.1:00 — 实现步骤
– 添加乐观锁插件
/**
* 添加乐观锁插件
* 常用于修改少的操作
* @return
*/
@Bean
public MybatisPlusInterceptor addOptimisticLocker(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return mybatisPlusInterceptor;
}
– 标识数据版本的字段
@Version
private Integer version;
– 使用
iUserTestMapper.update(userTest, userTestLambdaUpdateWrapper);
// Wrapper 不能复用 以下不能更新成功
iUserTestMapper.update(userTest2, userTestLambdaUpdateWrapper);
4:性能分析插件
4.1:00 — 分析Sql语句 以及执行时间
–
/**
* 添加性能分析插件
* @return
*/
@Bean
public MybatisPlusInterceptor addIllegalSql(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new IllegalSQLInnerInterceptor());
return mybatisPlusInterceptor;
}
/**
* 防止全表更新与删除插件
* @return
*/
@Bean
public MybatisPlusInterceptor addBlockAttack(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
return mybatisPlusInterceptor;
}
4.2:00 — 使用 p6spy 进行 分析Sql语句 以及执行时间分析
–
// 第一步 引入
<!-- https://mvnrepository.com/artifact/p6spy/p6spy -->
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.9.1</version>
</dependency>
// 第二步 配置数据源
spring:
datasource:
username: 用户名
password: 密码
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:mysql://ip:3306/mark_six?useUnicode=true&characterEncoding=UTF-8
// 第三步 添加 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
5:多租户SQL解析器
5.1:00 — 概念
– 一种软件架构技术,实现多用户环境下共用相同的系统或者程序并且确保用户之间数据隔离
5.2:03 — 方案
– 独立数据库,为不同用户提供单独的数据库,可定制性强,隔离级别最高,缺点维护成本高
– 所有用户共享database,每个用户使用单个scheme,逻辑数据隔离
– 共享数据库database,共享schema,共享表
5.2:00 — MP实现多租户SQL解析器
– 一:依赖插件 TenantLineInnerInterceptor
public MybatisPlusInterceptor addTenantLine(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
TenantLineInnerInterceptor tenantLineInnerInterceptor = new TenantLineInnerInterceptor();
tenantLineInnerInterceptor.setTenantLineHandler(new TenantLineHandler() {
@Override
public Expression getTenantId() {
//返回 租户信息
return new StringValue("江西阿兰德");
}
public String getTenantIdColumn() {
return "tenant_id";
}
public boolean ignoreTable(String tableName){
return false;
}
});
mybatisPlusInterceptor.addInnerInterceptor(tenantLineInnerInterceptor);
return mybatisPlusInterceptor;
}
5.4:00 — 特定SQL过滤
–
6:动态表名SQL解析器
6.1:00 — 用法
– 常用于分表后 操作 CURD
6.2:00 — 实现
public static ThreadLocal<String> myTableName = new ThreadLocal<>();
public MybatisPlusInterceptor addDynamicTableName(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();
HashMap<String, TableNameHandler> stringTableNameHandlerHashMap = new HashMap<>();
stringTableNameHandlerHashMap.put("user_test", new TableNameHandler() {
@Override
public String dynamicTableName(String sql, String tableName) {
System.out.println(tableName);
//返回null时 表名不替换
return myTableName.get();
}
});
dynamicTableNameInnerInterceptor.setTableNameHandlerMap(stringTableNameHandlerHashMap);
mybatisPlusInterceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);
return mybatisPlusInterceptor;
}
6.2:00 — 当对一个mapper方法添加了过滤器后 ,多租户sql解析器,动态表名sql解析器 将对该方法无效
7:SQL注入器
7.1:00 — 用法
– MP中的通用mapper方法也是添加到SQL注入器中
– 实现自定义通用mapper方法
7.2:00 — 实现
– 1:创建自定义方法的类
public class CnredfoxMathod extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
String sql = "delete from " + tableInfo.getTableName();
String methodName = "deleteAll";
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
return addDeleteMappedStatement(mapperClass, methodName, sqlSource);
}
}
– 2:创建注入器
– 3:在Mapper中加入自定义方法
3:传统ssm编程结构
3.1:04 — 步骤
– 01 在接口中写抽象方法
– 02 xml 或 注解 写sql
– 03 Service 中调用接口
– 04 Controller 中调用 Service 方法