mybatis-3.5.0使用插件拦截sql以及通用字段赋值

ops/2024/10/18 12:25:24/

1、添加插件配置类

java">import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.defaults.DefaultSqlSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import tk.mybatis.mapper.annotation.LogicDelete;
import tk.mybatis.mapper.annotation.Version;import java.io.File;
import java.lang.reflect.Field;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;@Component
@Intercepts({@Signature(type = Executor.class,method = "update",args = {MappedStatement.class, Object.class}
)})
public class MybatisInterception implements Interceptor {public MybatisInterception() {}public Object intercept(Invocation invocation) throws Throwable {MappedStatement mappedStatement = (MappedStatement)invocation.getArgs()[0];SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();Object parameter = invocation.getArgs()[1];if (parameter == null) {return invocation.proceed();} else {Field[] declaredFields = this.getAllFields(parameter);Date now = new Date();String userId = ContextUtil.getUserId();Integer dteptId = ContextUtil.getDteptId();Field[] var9 = declaredFields;if (SqlCommandType.DELETE.equals(sqlCommandType)) {BoundSql boundSql = mappedStatement.getBoundSql(parameter);//转换DELETE语句为逻辑删除语句String logicDeleteSql = convertToLogicDelete(boundSql.getSql());FieldUtils.writeField(boundSql, "sql", logicDeleteSql, true);MappedStatement newMappedStatement = new MappedStatement.Builder(mappedStatement.getConfiguration(),mappedStatement.getId(), new BoundSqlSqlSource(boundSql), mappedStatement.getSqlCommandType()).build();invocation.getArgs()[0] = newMappedStatement;return invocation.proceed();}if(parameter instanceof DefaultSqlSession.StrictMap) {DefaultSqlSession.StrictMap strictMap = (DefaultSqlSession.StrictMap) parameter;Object collection = strictMap.get("collection");if(collection instanceof Collection) {for (Object obj : ((Collection<Object>) collection)) {Field[] fields = this.getAllFields(obj);for (Field field : fields) {extracted(sqlCommandType, obj, now, userId, dteptId, field);}}}} else {for (Field field : var9) {extracted(sqlCommandType, parameter, now, userId, dteptId, field);}}return invocation.proceed();}}private void extracted(SqlCommandType sqlCommandType, Object parameter, Date now, String userId, Integer dteptId, Field field) throws IllegalAccessException {if (field.getAnnotation(NotAutoFill.class) == null) {Class clazz;if (SqlCommandType.INSERT.equals(sqlCommandType)) {if (field.getAnnotation(CreateTime.class) != null || Objects.equals(field.getName(), "createTime")) {field.setAccessible(true);field.set(parameter, now);}if (field.getAnnotation(Version.class) != null || Objects.equals(field.getName(), "version")) {field.setAccessible(true);field.set(parameter, 1);}if (field.getAnnotation(LogicDelete.class) != null || Objects.equals(field.getName(), "isDeleted")) {field.setAccessible(true);field.set(parameter, 0);}if (Objects.equals(field.getName(), "status")) {field.setAccessible(true);if (field.getType() == Integer.class) {field.set(parameter, 0);}}if (Objects.equals(field.getName(), "createBy")) {field.setAccessible(true);if (StringUtils.isNotEmpty(userId)) {clazz = field.getType();if (clazz == Integer.class) {field.set(parameter, Integer.parseInt(userId));} else {field.set(parameter, userId);}}}if (Objects.equals(field.getName(), "createDeptId")) {field.setAccessible(true);field.set(parameter, dteptId);}}if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {if (field.getAnnotation(UpdateTime.class) != null || Objects.equals(field.getName(), "updateTime")) {field.setAccessible(true);field.set(parameter, now);}if (Objects.equals(field.getName(), "updateBy")) {field.setAccessible(true);clazz = field.getType();if (StringUtils.isNotEmpty(userId)) {if (clazz == Integer.class) {field.set(parameter, Integer.parseInt(userId));} else {field.set(parameter, userId);}}}}}}public static Class<?> findEntityClassByTableName(String tableName, String basePackage) {ClassPathScanningCandidateComponentProvider scanner =new ClassPathScanningCandidateComponentProvider(false);scanner.addIncludeFilter(new AnnotationTypeFilter(Table.class));Set<BeanDefinition> beanDefinitions = scanner.findCandidateComponents(basePackage);for (BeanDefinition beanDefinition : beanDefinitions) {try {Class<?> clazz = Class.forName(beanDefinition.getBeanClassName());Table table = clazz.getAnnotation(Table.class);if (table != null && table.name().equals(tableName)) {return clazz;}} catch (ClassNotFoundException e) {continue;}}return null;}private String convertToLogicDelete(String sql) throws NoSuchFieldException {Pattern pattern = Pattern.compile("DELETE\\s+FROM\\s+(\w+)\\s+WHERE\\s+(.*)", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);Matcher matcher = pattern.matcher(sql);if (matcher.matches()) {String tableName = matcher.group(1); //获取表名Class<?> classByTableName = findEntityClassByTableName(tableName, "com.demo.entity");if(classByTableName == null) {return sql;}Field isDeletedField = getDeletedField(classByTableName);if(isDeletedField == null || isDeletedField.getAnnotation(LogicDelete.class) == null) {return sql;}String whereClause = matcher.group(2).trim(); //获取WHERE子句并去除两端空白字符// 构建逻辑删除的UPDATE语句StringBuilder logicDeleteSql = new StringBuilder();logicDeleteSql.append("UPDATE ").append(tableName).append(" SET ").append("is_deleted").append(" = ").append(1);//如果WHERE子句不为空或不全为空白字符,则加上WHERE子句if (!whereClause.isEmpty()) {logicDeleteSql.append(" WHERE ").append(whereClause);}return logicDeleteSql.toString();} else {// 如果SQL语句格式不匹配,则返回原始SQL或抛出异常,这里为了简单起见,直接返回原始 SQLreturn sql;}}public Object plugin(Object o) {return Plugin.wrap(o, this);}public void setProperties(Properties properties) {}private Field getDeletedField(Class clazz) {String isDeleted =  "isDeleted";Field isDeletedField = null;for (Class<?> c = clazz; c != null; c = c.getSuperclass()) {Field[] declaredFields = c.getDeclaredFields();for (Field field : declaredFields) {if(field.getName().equals(isDeleted)) {isDeletedField = field;break;}}}return isDeletedField;}private Field[] getAllFields(Object object) {Class<?> clazz = object.getClass();ArrayList fieldList;for(fieldList = new ArrayList(); clazz != null; clazz = clazz.getSuperclass()) {fieldList.addAll(new ArrayList(Arrays.asList(clazz.getDeclaredFields())));}Field[] fields = new Field[fieldList.size()];fieldList.toArray(fields);return fields;}public class BoundSqlSqlSource implements SqlSource {private BoundSql boundSql;public BoundSqlSqlSource(BoundSql boundSql) {this.boundSql = boundSql;}@Overridepublic BoundSql getBoundSql(Object parameterObject) {return boundSql;}}
}

2、修改mybatis-config.xml

切身经历但不一定是真相:项目已经有拦截器和mybatis-config.xml比如在公共依赖中,再新建mybatis-config.xml会存在覆盖xml配置的情况,虽然拦截器都生效了。如果命名为mybatis-config-xxx.xml等拦截器就没有生效。通过采用配置类的形式,暂时未发现不良影响。

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!-- 插件配置... --><plugins><plugin interceptor="xxx.xxx.MybatisInterceptor"><!-- 插件可以配置属性,如果需要的话 --><!-- <property name="someProperty" value="someValue"/> --></plugin></plugins><!-- 其他配置... -->
</configura

3、配置类,一般叫MybatisConfig,显然不可重名。于是另起炉灶。

java">@Configuration
@AutoConfigureAfter({PageHelperAutoConfiguration.class})
public class MybatisFeatureConfig {@Autowiredprivate List<SqlSessionFactory> sqlSessionFactoryList;public MybatisFeatureConfig() {}@PostConstructpublic void addMyInterceptor() {MybatisInterception e = new MybatisInterception();Iterator var2 = this.sqlSessionFactoryList.iterator();while(var2.hasNext()) {SqlSessionFactory sqlSessionFactory = (SqlSessionFactory)var2.next();sqlSessionFactory.getConfiguration().addInterceptor(e);}}
}

4、基本思路,如果使用mybatis-plus我尚未注意过批量操作的问题,所以不知会有什么坑


http://www.ppmy.cn/ops/15610.html

相关文章

docker教程(详细)

0 背景 软件开发最大的麻烦事之一&#xff0c;就是环境配置。环境配置如此麻烦&#xff0c;换一台机器&#xff0c;就要重来一次&#xff0c;旷日费时。很多人想到&#xff0c;能不能从根本上解决问题&#xff0c;软件可以带环境安装&#xff1f;也就是说&#xff0c;安装的时…

Leetcode 150:逆波兰表达式求值

给你一个字符串数组 tokens &#xff0c;表示一个根据 逆波兰表示法 表示的算术表达式。 请你计算该表达式。返回一个表示表达式值的整数。 示例 1&#xff1a; 输入&#xff1a;tokens ["2","1","","3","*"] 输出&…

桌面运维类面试非技术问题

1、计算机系统卡顿怎么处理 计算机系统卡顿可以通过硬件升级、系统优化、软件优化和安全防护等措施可解决。 包括增加内存条、更换固态硬盘、磁盘清理、关闭后台进程、禁用开机启动项、重装系统和更新驱动程序等。 卸载不常用软件、使用轻量级软件和开启防火墙也是有效的方法。…

浅谈操作系统中的重要概念——进程

文章目录 一、什么是程序&#xff1f;二、什么是进程&#xff1f;三、进程与程序有什么区别&#xff1f;四、OS是如何管理进程的4.1、使用 结构体 进行描述进程4.2 、使用数据结构组织众多进程4.3、PCB4.3.1、PCB 里有哪些属性4.3.1.1 pid4.3.1.2 内存指针4.3.1.3 文件描述符表…

shell--for循环

1.带列表for循环 语法格式 for 循环变量 in 列表 do执行语句... done 在上面的语法中&#xff0c;循环变量是每次循环时得到的列表的某一个数据&#xff0c;当循环一次结束后&#xff0c;再获取另一个数&#xff0c;然后再执行 do 里面的语句&#xff0c;依次类推&#xff0…

golang面试题:拷贝大切片一定比小切片代价大吗?

问题 拷贝大切片一定比小切片代价大吗&#xff1f; 怎么答 并不是&#xff0c;所有切片的大小相同&#xff1b;三个字段&#xff08;一个 uintptr&#xff0c;两个int&#xff09;。切片中的第一个字是指向切片底层数组的指针&#xff0c;这是切片的存储空间&#xff0c;第二…

SystemC 等待异步事件解决方案

本文为实现 SystemC 响应异步事件 解决方案。 应用场景&#xff1a; SystemC是一个支持系统事务级、行为级建模的开源的C library&#xff1b; 我们将SystemC仿真的模拟叫做模拟器。在很多场景下&#xff0c;模拟器要保持alive&#xff0c;等待异步async事件&#xff0c;做出…

【Java并发知识总结 | 第七篇】Java并发相关概念总结(程序/进程/线程、并行/并发、同步/异步、死锁/避免、线程安全/三大特性)

文章目录 7.并发相关概念总结&#xff08;程序/进程/线程、并行/并发、同步/异步、死锁/避免、线程安全/三大特性&#xff09;7.1程序、进程与线程7.2并行和并发7.3同步和异步7.4什么是死锁&#xff1f;如何避免&#xff1f;7.5何为线程安全&#xff1f;以及三大特性 7.并发相关…