mybatis-plus使用和原理剖析之逻辑删除

系列文章

mybatis-plus使用和原理剖析之条件构造器

文章目录

    • 一、官方说明
    • 二、使用方法
    • 三、原理剖析
      • 1.SQL注入器原理
      • 2.SelectById
      • 3.DeleteById
      • 4.总结
    • 四、风险评估

一、官方说明

逻辑删除

说明:

只对自动注入的 sql 起效:

  • 插入: 不作限制
  • 查找: 追加 where 条件过滤掉已删除数据,且使用 wrapper.entity 生成的 where 条件会忽略该字段
  • 更新: 追加 where 条件防止更新到已删除数据,且使用 wrapper.entity 生成的 where 条件会忽略该字段
  • 删除: 转变为 更新

例如:

  • 删除: update user set deleted=1 where id = 1 and deleted=0
  • 查找: select id,name,deleted from user where deleted=0

字段类型支持说明:

  • 支持所有数据类型(推荐使用 Integer,Boolean,LocalDateTime)
  • 如果数据库字段使用datetime,逻辑未删除值和已删除值支持配置为字符串null,另一个值支持配置为函数来获取值如now()

附录:

  • 逻辑删除是为了方便数据恢复和保护数据本身价值等等的一种方案,但实际就是删除。
  • 如果你需要频繁查出来看就不应使用逻辑删除,而是以一个状态去表示。

二、使用方法

  • 全局配置
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)
  • 局部配置
    /*** 逻辑删除 1-已删除, 0-未删除*/@TableField(value = "is_deleted")@TableLogic(value = "0", delval = "1")private String isDeleted;

三、原理剖析

官方说明中很重要的一点是逻辑删除"只对自动注入的 sql 起效",也就是说XML中自定义的SQL不会自动拼接逻辑删除条件也不会将物理删除更改为逻辑删除。

为什么不可以像分页插件、多租户插件以及乐观锁插件一样通过SQL拦截器实现哪?问题在于逻辑删除本质是update语句,物理删除是delete语句,没有见过SQL拦截器直接改变DML 语句类型的(我孤陋寡闻也说不定😂)

前面讲了逻辑删除"只对自动注入的 sql 起效",其中“插入”不做限制,“查找”追加逻辑删除谓语片段,“物理删除”改为“逻辑删除”,下面主要看下SelectByIdDeleteById SQL注入方法。

1.SQL注入器原理

  • SQL注入器
public class DefaultSqlInjector extends AbstractSqlInjector {@Overridepublic List<AbstractMethod> getMethodList(Class<?> mapperClass) {return Stream.of(new Insert(),new Delete(),new DeleteByMap(),new DeleteById(),new DeleteBatchByIds(),new Update(),new UpdateById(),new SelectById(),new SelectBatchByIds(),new SelectByMap(),new SelectOne(),new SelectCount(),new SelectMaps(),new SelectMapsPage(),new SelectObjs(),new SelectList(),new SelectPage()).collect(toList());}
}
  • SQL方法字符串模板
package com.baomidou.mybatisplus.core.enums;/*** MybatisPlus 支持 SQL 方法*/
public enum SqlMethod {/*** 插入*/INSERT_ONE("insert", "插入一条数据(选择字段插入)", "<script>\nINSERT INTO %s %s VALUES %s\n</script>"),UPSERT_ONE("upsert", "Phoenix插入一条数据(选择字段插入)", "<script>\nUPSERT INTO %s %s VALUES %s\n</script>"),/*** 删除*/DELETE_BY_ID("deleteById", "根据ID 删除一条数据", "<script>\nDELETE FROM %s WHERE %s=#{%s}\n</script>"),DELETE_BY_MAP("deleteByMap", "根据columnMap 条件删除记录", "<script>\nDELETE FROM %s %s\n</script>"),DELETE("delete", "根据 entity 条件删除记录", "<script>\nDELETE FROM %s %s %s\n</script>"),DELETE_BATCH_BY_IDS("deleteBatchIds", "根据ID集合,批量删除数据", "<script>\nDELETE FROM %s WHERE %s IN (%s)\n</script>"),/*** 逻辑删除*/LOGIC_DELETE_BY_ID("deleteById", "根据ID 逻辑删除一条数据", "<script>\nUPDATE %s %s WHERE %s=#{%s} %s\n</script>"),LOGIC_DELETE_BY_MAP("deleteByMap", "根据columnMap 条件逻辑删除记录", "<script>\nUPDATE %s %s %s\n</script>"),LOGIC_DELETE("delete", "根据 entity 条件逻辑删除记录", "<script>\nUPDATE %s %s %s %s\n</script>"),LOGIC_DELETE_BATCH_BY_IDS("deleteBatchIds", "根据ID集合,批量逻辑删除数据", "<script>\nUPDATE %s %s WHERE %s IN (%s) %s\n</script>"),/*** 修改*/UPDATE_BY_ID("updateById", "根据ID 选择修改数据", "<script>\nUPDATE %s %s WHERE %s=#{%s} %s\n</script>"),UPDATE("update", "根据 whereEntity 条件,更新记录", "<script>\nUPDATE %s %s %s %s\n</script>"),/*** 逻辑删除 -> 修改*/LOGIC_UPDATE_BY_ID("updateById", "根据ID 修改数据", "<script>\nUPDATE %s %s WHERE %s=#{%s} %s\n</script>"),/*** 查询*/SELECT_BY_ID("selectById", "根据ID 查询一条数据", "SELECT %s FROM %s WHERE %s=#{%s} %s"),SELECT_BY_MAP("selectByMap", "根据columnMap 查询一条数据", "<script>SELECT %s FROM %s %s\n</script>"),SELECT_BATCH_BY_IDS("selectBatchIds", "根据ID集合,批量查询数据", "<script>SELECT %s FROM %s WHERE %s IN (%s) %s</script>"),SELECT_ONE("selectOne", "查询满足条件一条数据", "<script>%s SELECT %s FROM %s %s %s\n</script>"),SELECT_COUNT("selectCount", "查询满足条件总记录数", "<script>%s SELECT COUNT(%s) FROM %s %s %s\n</script>"),SELECT_LIST("selectList", "查询满足条件所有数据", "<script>%s SELECT %s FROM %s %s %s\n</script>"),SELECT_PAGE("selectPage", "查询满足条件所有数据(并翻页)", "<script>%s SELECT %s FROM %s %s %s\n</script>"),SELECT_MAPS("selectMaps", "查询满足条件所有数据", "<script>%s SELECT %s FROM %s %s %s\n</script>"),SELECT_MAPS_PAGE("selectMapsPage", "查询满足条件所有数据(并翻页)", "<script>\n %s SELECT %s FROM %s %s %s\n</script>"),SELECT_OBJS("selectObjs", "查询满足条件所有数据", "<script>%s SELECT %s FROM %s %s %s\n</script>");private final String method;private final String desc;private final String sql;SqlMethod(String method, String desc, String sql) {this.method = method;this.desc = desc;this.sql = sql;}public String getMethod() {return method;}public String getDesc() {return desc;}public String getSql() {return sql;}}
  • 调用链

=> SqlSessionFactory 若Spring容器总注入ISqlInjector.class 类型Bean则替换默认的DefaultSqlInjector

com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration#sqlSessionFactory

=> 默认的DefaultSqlInjector值来源


=> MybatisPlus自动配置类

com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration

=> 全局默认配置中默认的SQL注入器 private ISqlInjector sqlInjector = new DefaultSqlInjector();

com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils#defaults

=> 默认的SQL注入器 重点 (怎样自定义SQL注入器?)

com.baomidou.mybatisplus.core.injector.DefaultSqlInjector

=> SQL注入器过程

com.baomidou.mybatisplus.core.injector.AbstractSqlInjector#inspectInject

参考我的另一篇文章中的“TableInfo初始化过程分析”部分

mybatis-plus使用和原理剖析之条件构造器

=> AbstractMethod

com.baomidou.mybatisplus.core.injector.AbstractMethod#inject

=> SelectById

com.baomidou.mybatisplus.core.injector.methods.SelectById#injectMappedStatement

2.SelectById

public class SelectById extends AbstractMethod {@Overridepublic MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {SqlMethod sqlMethod = SqlMethod.SELECT_BY_ID;SqlSource sqlSource = new RawSqlSource(configuration, String.format(sqlMethod.getSql(),sqlSelectColumns(tableInfo, false),tableInfo.getTableName(), tableInfo.getKeyColumn(), tableInfo.getKeyProperty(),// 拼接逻辑删除谓语条件tableInfo.getLogicDeleteSql(true, true)), Object.class);return this.addSelectMappedStatementForTable(mapperClass, getMethod(sqlMethod), sqlSource, tableInfo);}
}

SQL字符串模板 SELECT_BY_ID("selectById", "根据ID 查询一条数据", "SELECT %s FROM %s WHERE %s=#{%s} %s")

=> 获取逻辑删除字段的 sql 脚本

com.baomidou.mybatisplus.core.metadata.TableInfo#getLogicDeleteSql

=> 格式化SQL脚本

com.baomidou.mybatisplus.core.metadata.TableInfo#formatLogicDeleteSql
    private String formatLogicDeleteSql(boolean isWhere) {final String value = isWhere ? logicDeleteFieldInfo.getLogicNotDeleteValue() : logicDeleteFieldInfo.getLogicDeleteValue();if (isWhere) {if (NULL.equalsIgnoreCase(value)) {return logicDeleteFieldInfo.getColumn() + " IS NULL";} else {return logicDeleteFieldInfo.getColumn() + EQUALS + String.format(logicDeleteFieldInfo.isCharSequence() ? "'%s'" : "%s", value);}}final String targetStr = logicDeleteFieldInfo.getColumn() + EQUALS;if (NULL.equalsIgnoreCase(value)) {return targetStr + NULL;} else {return targetStr + String.format(logicDeleteFieldInfo.isCharSequence() ? "'%s'" : "%s", value);}}

以上代码可以看到,逻辑删除字段支持所有类型,其中字符串类型值使用单引号拼接,其余情况直接用拼接

3.DeleteById

public class DeleteById extends AbstractMethod {@Overridepublic MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {String sql;SqlMethod sqlMethod = SqlMethod.LOGIC_DELETE_BY_ID;// 逻辑删除if (tableInfo.isWithLogicDelete()) {sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), sqlLogicSet(tableInfo),tableInfo.getKeyColumn(), tableInfo.getKeyProperty(),tableInfo.getLogicDeleteSql(true, true));SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, Object.class);return addUpdateMappedStatement(mapperClass, modelClass, getMethod(sqlMethod), sqlSource);} else {// 物理删除sqlMethod = SqlMethod.DELETE_BY_ID;sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), tableInfo.getKeyColumn(),tableInfo.getKeyProperty());SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, Object.class);return this.addDeleteMappedStatement(mapperClass, getMethod(sqlMethod), sqlSource);}}
}

物理删除SQL字符串模板 DELETE_BY_ID("deleteById", "根据ID 删除一条数据", "\nDELETE FROM %s WHERE %s=#{%s}\n")

逻辑删除SQL字符串模板 LOGIC_DELETE_BY_ID("deleteById", "根据ID 逻辑删除一条数据", "\nUPDATE %s %s WHERE %s=#{%s} %s\n")

4.总结

Mybatis-Plus TableInfo是一个极为重要的类,它在项目工程启动时通过com.baomidou.mybatisplus.core.metadata.TableInfoHelper#initTableInfo(org.apache.ibatis.builder.MapperBuilderAssistant, java.lang.Class)工具类初始化而来。

四、风险评估

前面我们已经了解到逻辑删除的工作机制,但该实现方式在极端情况下可能会存在SQL注入的风险,造成数据被恶意更改、数据库被删等。

触发条件(很难)

  • 允许多语句执行

jdbc.url中配置 allowMultiQueries=true

  • 逻辑删除值配置在不安全环境中(如使用了配置中心但存在弱密码或者权限控制不当导致属性值被恶意更改)
mybatis-plus:global-config:db-config:logic-delete-field: isDeleted # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)logic-delete-value: 1 # 逻辑已删除值(默认为 1)logic-not-delete-value: 0 or 1=1 # 逻辑未删除值(默认为 0)

    /*** 逻辑删除 1-已删除, 0-未删除*/@TableField(value = "is_deleted")@TableLogic(value = "0 or 1=1", delval = "1")private Boolean isDeleted;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/554840.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全国计算机英语四六级准考证打印准考证号,2017全国大学生英语四六级准考证打印...

2017年上半年度CET考试时间为6月17日&#xff0c;同学们打印准考证了吗?为方便大家打印四六级准考证&#xff0c;yjbys小编为大家分享英语四级考试准考证打印官方主页入口如下&#xff1a;2017年上半年度CET考试时间及报名方式一、考试科目及时间1.笔试考试时间日期(6月17日)考…

Centos7配置Jenkins

Centos7配置Jenkins jenkins 官方下载地址&#xff1a;https://mirrors.jenkins-ci.org/redhat/ maven 官方下载地址&#xff1a;https://archive.apache.org/dist/maven/maven-3/ tomcat 官方下载地址&#xff1a;https://tomcat.apache.org/download-90.cgi 1、配置jdk …

学计算机的写论文题目,经典本科计算机论文选题 本科计算机论文题目怎样取...

【100道】经典本科计算机论文选题,每一个论文题目都是精选出来的,看了后定能知晓本科计算机论文题目怎样取等相关写作技巧,让本科计算机论文写作轻松起来&#xff01;一、比较好写的本科计算机论文题目:1、应用型本科计算机的数学实验教学研究2、对应用型本科计算机网络方向实践…

Collectors.groupingBy()进行分组时,分组值存在null值会报NPE(空指针)错误,使用时要注意

问题分析&#xff1a; 1、使用Collectors.groupingBy()进行分组时&#xff0c;分组值存在null值。 List<String> strList new ArrayList<>(Arrays.asList("11", "12", "13", null, null)); Map<String, List<String>>…

计算机硬盘用u盘维修,U盘装机大师修复磁盘坏道详细教程

我们都知道当我们的磁盘使用的时间久了就会容易出现各种问题&#xff0c;然而硬盘的坏道是最常见的问题之一。关于磁盘出现坏道有很多原因&#xff0c;诸如硬盘本身质量问题&#xff0c;老化&#xff0c;使用不当等等。我们的硬盘内存太小也会导致应用软件对硬盘频繁访问&#…

Linux中Minio安装与启动

Minio是在Apache License v2.0下发布的对象存储服务器。它与Amazon S3云存储服务兼容。它最适合存储非结构化数据&#xff0c;如照片&#xff0c;视频&#xff0c;日志文件&#xff0c;备份和容器/ VM映像。对象的大小可以从几KB到最大5TB。Minio服务器足够轻&#xff0c;可以与…

多屏幕炒股计算机配置,多屏幕股票交易计算机配置建议使用i59400F计算机主机配置(最多六个屏幕)...

在经历了几年的熊市股市之后&#xff0c;2019年股市似乎有所回升. 最近&#xff0c;许多用户询问多屏股票交易计算机的配置&#xff0c;例如三屏&#xff0c;四屏&#xff0c;和六屏. 实际上&#xff0c;与普通计算机的最大区别是该图形卡需要配备多屏幕图形卡. 一台计算机可以…

List.removeIf():删除集合中满足给定条件的所有元素

今天偶然间发现了Collection在1.8新增了一个removeIf(Predicate<? super E> filter)方法&#xff0c;能够实现面试官们常问的&#xff1a;如何一边遍历&#xff0c;一边删除。 首先是源码 /*** Removes all of the elements of this collection that satisfy the given…

计算机专业论文docx,计算机系毕业论文范文-20210606000812.docx-原创力文档

计算机系毕业论文范文一&#xff1a;计算机技术在工程项目管理中的应用推动国家经济发展的核心是科学技术的进步。有效推动科学技术进步的关键在于创新&#xff0c;这是国家经济与社会前进的有效动力&#xff0c;在相关产业领域也发挥着非常重要作用&#xff0c;作为国家经济的…

小学二年级上学期计算机教案,小学数学二年级上册分苹果教案设计

小学数学二年级上册分苹果教案设计〖教学目标1.经历分苹果等实际操作&#xff0c;初步体会有余数除法与生活的密切联系&#xff0c;进一步体会除法的意义。2.通过实际操作&#xff0c;抽象出有余数除法的书写格式&#xff0c;并体会余数一定要比除数小。〖教材分析分苹果是二年…

生物计算机的主要原材料是(),新材料为生物计算机打造“神经元”和“突触”...

一项最新研究利用复杂的氧化物&#xff0c;打造出了与神经元和突触相似的元件。图片来自pixabay.com虽然电脑的计算速度比人脑快&#xff0c;但在物体识别任务等方面&#xff0c;人脑还是更胜一筹。除此之外&#xff0c;人脑耗费的能量也远低于电脑。大脑的运作方式可以在一定程…

hotmail接收邮件服务器(pop),Microsoft微软邮箱 outlook、hotmail 打开pop和imap的方法

分享个微软邮箱 outlook、hotmail 打开pop和imap的方法只有打开了pop或者imap &#xff0c; foxmail一类的邮件管理客户端才能正常收邮件&#xff1b;打开了smtp才能正常发邮件。设置方法如图&#xff1a;1.登录进去账户以后&#xff0c;点击右上角的设置&#xff0c;齿轮图标&…

MySQL 索引 是如何提高 查询效率 的?

前言 我们都知道当查询数据库变慢时&#xff0c;需要建索引去优化。但是只知道索引能优化显然是不够的&#xff0c;我们更应该知道索引的原理&#xff0c;因为不是加了索引就一定会提升性能。那么接下来就一起探索MYSQL索引的原理吧 什么是索引 索引其实是一种能高效帮助MYS…

yii2 ajax访问控制器,如何在yii2中运行控制器动作作为ajax

使用模态来解决它。use yii\bootstrap\Modal;use johnitvn\ajaxcrud\CrudAsset;Html::a(Custom, [custom], [role>modal-remote, title>Custom]);在页面底部.."id">"ajaxCrudModal","footer">"",// always need it for jq…

ajax onerror code,Ajax请求'onError'处理程序

我的网站上有一项功能&#xff1a;删除不刷新页面。用户只需按下删除&#xff0c;浏览器就会发送Ajax请求。它会用ID参数加载delete脚本。Ajax请求onError处理程序一切正常。但由于数据库的参照完整性&#xff0c;它不是很好。例如&#xff0c;有可能删除一些人居住的街道。我想…

FreeMarker详细介绍

FreeMarker 1. 主要内容 2.FreeMarker概述 2.1. FreeMarker概念 FreeMarker 是一款 模板引擎&#xff1a; 即一种基于模板和要改变的数据&#xff0c; 并用来生成输出文本(HTML网页&#xff0c;电子邮件&#xff0c;配置文件&#xff0c;源代码等)的通用工具。 是一个Java类…

微信游戏奇迹暖暖选取服务器失败,奇迹暖暖微信登录授权失败

《远征》即将推出衣橱系统 或将成为网游版奇迹暖暖双十一狂欢刚刚落幕&#xff0c;这几天的状态都将在等快递、拆快递中度过&#xff0c;而你剁手而来的衣服&#xff0c;是否需要一个大大的衣帽间呢?《奇迹重生》持之以恒玩家的专属嘉奖坚持不懈的努力才能最终成为真正的强者!…

SpringBoot整合Freemarker导出word文档表格

freemarker模板里面的template.process()方法里传入的第一个参数Object类型&#xff0c;如果是一个实体类对象在模板上怎么进行渲染&#xff0c;将实体类的值取出 freemarker会调用ObjectWrapper对传入的对象进行warp&#xff0c;具体类型在代码里面用instanceof进行判断。一般…

ambari 修改服务器名,Ambari修改主页面方法

分享下Ambari修改主页面方法&#xff0c;希望对大家有用。[roothdp159 ambari-web]# brunch watch --serverOct 10:22:43 - info: application started on http://localhost:3333/Oct 10:22:47 - info: compiled 891 files into 5 files, copied 260 in 3988msOct 10:23:12 - i…

mybatis plus之自定义SQL查询

注解查询 public interface UserMapper extends BaseMapper<User> {Select("select * from user ${ew.customSqlSegment}")List<User> selectAll(Param(Constants.WRAPPER) Wrapper<User> wrapper); }使用XML查询 maven 资源 默认只有resources…