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

1、添加插件配置类

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或抛出异常,这里为了简单起见,直接返回原始 SQL
            return 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;
        }

        @Override
        public 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,显然不可重名。于是另起炉灶。

@Configuration
@AutoConfigureAfter({PageHelperAutoConfiguration.class})
public class MybatisFeatureConfig {
    @Autowired
    private List<SqlSessionFactory> sqlSessionFactoryList;

    public MybatisFeatureConfig() {
    }

    @PostConstruct
    public 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我尚未注意过批量操作的问题,所以不知会有什么坑

5、遗留问题,采用替换sql的方式处理逻辑删除,会导致一些在xml里面自定义非逻辑删除语句变成逻辑删除。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值