mybatis-plus使用及逻辑删除设置更新用户

459 阅读2分钟
  1. mybatis-plus逻辑删除设置 在po里面增加注解:
/**
 * 状态,0可用,1不可用
 */
@TableLogic(value = "0", delval = "1")
private Integer deleted;
  1. mybatis-plus拦截器配置
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    // 乐观锁
    interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
    // 分页插件
    interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
    return interceptor;
}
  1. 配置文件
mybatis-plus:
  configuration:
    cache-enabled: true
    lazyLoadingEnabled: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true
    multipleResultSetsEnabled: true
  mapper-locations: classpath*:/mapper/**/*.xml
  typeAliasesPackage: com.abel.ds.entity.po
  1. 编写拦截器方法
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.sql.ast.SQLName;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr;
import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem;
import com.alibaba.druid.sql.ast.statement.SQLUpdateStatement;
import com.alibaba.druid.sql.parser.SQLStatementParser;
import com.alibaba.druid.util.JdbcUtils;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
public class LogicDeleteHelper {
    private static final Logger logger = LoggerFactory.getLogger(LogicDeleteHelper.class);
    private Map<String, List<String>> columnMap = new ConcurrentHashMap<>();

    public static MappedStatement newMappedStatement(MappedStatement ms, SqlSource newSqlSource, Object parameterObject) {
        MappedStatement.Builder builder =
                new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType());
        builder.resource(ms.getResource());
        BoundSql boundSql = newSqlSource.getBoundSql(parameterObject);
        builder.fetchSize(ms.getFetchSize());
        builder.statementType(ms.getStatementType());
        builder.keyGenerator(ms.getKeyGenerator());
        if (ms.getKeyProperties() != null && ms.getKeyProperties().length != 0) {
            StringBuilder keyProperties = new StringBuilder();
            for (String keyProperty : ms.getKeyProperties()) {
                keyProperties.append(keyProperty).append(",");
            }
            keyProperties.delete(keyProperties.length() - 1, keyProperties.length());
            builder.keyProperty(keyProperties.toString());
        }
        builder.timeout(ms.getTimeout());
        builder.parameterMap(ms.getParameterMap());
        builder.resultMaps(ms.getResultMaps());
        builder.resultSetType(ms.getResultSetType());
        builder.cache(ms.getCache());
        builder.flushCacheRequired(ms.isFlushCacheRequired());
        builder.useCache(ms.isUseCache());

        return builder.build();
    }

    public void setLogicUser(Invocation invocation) {
        boolean isSqlChange = false;
        final Object[] args = invocation.getArgs();
        Object parameterObject = args[1];
        MappedStatement statement = (MappedStatement) args[0];
        String sql = MybatisUtils.getSqlByInvocation(invocation);
        SQLStatementParser sqlStatementParser = new SQLStatementParser(sql);
        SQLStatement sqlStatement = null;
        sqlStatement = sqlStatementParser.parseStatement();
        if (!(sqlStatement instanceof SQLUpdateStatement)) {
            logger.warn("不是update操作,{}", sql);
            return;
        }
        SQLUpdateStatement sqlUpdateStatement = (SQLUpdateStatement) sqlStatement;
        SQLName sqlName = sqlUpdateStatement.getTableName();
        String tableName = sqlName.getSimpleName();
        List<String> columns = columnMap.get(tableName);
        if (CollectionUtils.isEmpty(columns)) {
            DataSource dataSource = statement.getConfiguration().getEnvironment().getDataSource();
            columns = populateColumns(dataSource, statement, tableName);
        }
        if (CollectionUtils.isEmpty(columns)) {
            logger.warn("找不到表的映射字段,{}", tableName);
            return;
        }
        BoundSql boundSql = statement.getBoundSql(parameterObject);
        List<ParameterMapping> oldParams = boundSql.getParameterMappings();
        List<ParameterMapping> newParams = new ArrayList<>();
        Map<String, Object> newAdditionalParameters = new HashMap<>();
        String userCode = UserUtils.getCodeFromThreadLocal("userCode");
        String userName = UserUtils.getCodeFromThreadLocal("userName");
        if (CollectionUtils.isEmpty(sqlUpdateStatement.getItems())) {
            return;
        }
        List<String> originColumns = getOriginColumns(sqlUpdateStatement);
//        sqlUpdateStatement.getItems().get(0).get
        int cursor = 0;
        for (SQLUpdateSetItem item : sqlUpdateStatement.getItems()) {
            if (item.getValue() instanceof SQLVariantRefExpr) {
                newParams.add(oldParams.get(cursor));
                cursor++;
            }
        }
        if (UserUtils.isUserValid(userCode) && columns.contains("updater_code") && !originColumns.contains("updater_code") && originColumns.contains("deleted")) {
            SQLUpdateSetItem sqlUpdateSetItem = new SQLUpdateSetItem();
            sqlUpdateSetItem.setColumn(new SQLIdentifierExpr("updater_code"));
            sqlUpdateSetItem.setValue(new SQLVariantRefExpr("?"));
            ParameterMapping parameterMapping = populateParameterMapping(statement, "updaterCode");
            newParams.add(parameterMapping);
            newAdditionalParameters.put("updaterCode", userCode);
            sqlUpdateStatement.addItem(sqlUpdateSetItem);
            isSqlChange = true;
        }
        if (UserUtils.isUserValid(userName) && columns.contains("updater_name") && !originColumns.contains("updater_name") && originColumns.contains("deleted")) {
            SQLUpdateSetItem sqlUpdateSetItem = new SQLUpdateSetItem();
            sqlUpdateSetItem.setColumn(new SQLIdentifierExpr("updater_name"));
            sqlUpdateSetItem.setValue(new SQLVariantRefExpr("?"));
            ParameterMapping parameterMapping = populateParameterMapping(statement, "updaterName");
            newParams.add(parameterMapping);
            newAdditionalParameters.put("updaterName", userCode);
            sqlUpdateStatement.addItem(sqlUpdateSetItem);
            isSqlChange = true;
        }
        for (int i = cursor; i < oldParams.size(); i++) {
            newParams.add(oldParams.get(i));
        }
        if (!isSqlChange) {
            return;
        }
        sql = sqlUpdateStatement.toString();
        try {
            resetSqlInvocation(statement, invocation, sql, newAdditionalParameters, newParams, parameterObject);
        } catch (SQLException throwables) {
            logger.error(throwables.getMessage(), throwables);
        }
    }

    protected void resetSqlInvocation(MappedStatement ms, Invocation invocation, String sql, Map<String, Object> newAdditionalParameters, List<ParameterMapping> newParams, Object parameterObject) throws SQLException {
        final Object[] args = invocation.getArgs();
        MappedStatement statement = (MappedStatement) args[0];
        BoundSql boundSql = statement.getBoundSql(parameterObject);
        Field field = null;
        try {
            field = boundSql.getClass().getDeclaredField("additionalParameters");
            field.setAccessible(true);
            Map<String, Object> additionalParameters = (Map<String, Object>) field.get(boundSql);
            field = boundSql.getClass().getDeclaredField("parameterMappings");
            field.setAccessible(true);
            field.set(boundSql, newParams);
            additionalParameters.putAll(newAdditionalParameters);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        MappedStatement newStatement = newMappedStatement(statement, new BoundSqlSqlSource(boundSql), parameterObject);
        MetaObject msObject = MetaObject.forObject(newStatement, new DefaultObjectFactory(), new DefaultObjectWrapperFactory(), new DefaultReflectorFactory());
        msObject.setValue("sqlSource.boundSql.sql", sql);
        args[0] = newStatement;
    }

    private ParameterMapping populateParameterMapping(MappedStatement ms, String property) {
        ParameterMapping.Builder builder =
                new ParameterMapping.Builder(ms.getConfiguration(), property, String.class);
        ParameterMapping parameterMapping = builder.build();
        return parameterMapping;
    }

    private List<String> getOriginColumns(SQLUpdateStatement sqlUpdateStatement) {
        List<String> columns = new ArrayList<>();
        if (CollectionUtils.isEmpty(sqlUpdateStatement.getItems())) {
            return columns;
        }
        for (SQLUpdateSetItem item : sqlUpdateStatement.getItems()) {
            columns.add(item.getColumn().toString());
        }
        return columns;
    }

    private List<String> populateColumns(DataSource dataSource, MappedStatement statement, String tableName) {
        String dbType = null;
        if (dataSource instanceof DruidDataSource) {
            DruidDataSource druidDataSource = (DruidDataSource) dataSource;
//            String dataSourceName = druidDataSource.getName();
            dbType = druidDataSource.getDbType();
        }
        Connection connection = null;
        try {
            connection = dataSource.getConnection();
            String catalog = connection.getCatalog();
            DatabaseMetaData metaData = connection.getMetaData();
            String schema = connection.getSchema();
            List<String> columns = getAllColumns(catalog, schema, dbType, tableName, metaData);
            if (!CollectionUtils.isEmpty(columns)) {
                columnMap.put(tableName, columns);
                return columns;
            } else {
                columnMap.put(tableName, new ArrayList<>());
            }
        } catch (SQLException e) {
            logger.error(e.getMessage(), e);
        }
        JdbcUtils.close(connection);
        return new ArrayList<>();
    }

    public List<String> getAllColumns(String catalog, String schema, String dbType, String tableName, DatabaseMetaData metaData) throws SQLException {
        String columnName;
        String tableName2 = tableName;
        if ("oracle".equals(dbType)) {
            tableName2 = tableName.toUpperCase();
        }
        ResultSet colRet = metaData.getColumns(catalog, schema, tableName2, "%");
        List<String> columns = new ArrayList<>();
        while (colRet.next()) {
            columnName = colRet.getString("COLUMN_NAME");
            columns.add(columnName);
        }
        return columns.stream().distinct().collect(Collectors.toList());
    }
}
  1. mybatis拦截器
@ConditionalOnProperty(prefix = "userset", value = "enabled", matchIfMissing = false)
@Component
@Intercepts({@Signature(type = Executor.class, method = "query",
        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
        , @Signature(type = Executor.class, method = "query",
        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class})
        , @Signature(type = Executor.class, method = "queryCursor",
        args = {MappedStatement.class, Object.class, RowBounds.class})
        , @Signature(type = Executor.class, method = "update",
        args = {MappedStatement.class, Object.class})
})
public class OneMybatisInterceptor implements Interceptor {

    private static final Logger logger = LoggerFactory.getLogger(OneMybatisInterceptor.class);
    @Autowired
    Properties Properties;
    @Autowired(required = false)
    SqlParserHelper sqlParserHelper;
    @Autowired
    LogicDeleteHelper logicDeleteHelper;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement statement = (MappedStatement) invocation.getArgs()[0];
        SqlCommandType sqlCommandType = statement.getSqlCommandType();
        //设置数据库默认值创建人,创建人名称,修改人,修改人名称
        setDefaultValue(invocation, sqlCommandType);
        //逻辑删除自动加上用户
        if (Properties.isEnableLogicDeleteUser() && SqlCommandType.UPDATE == sqlCommandType) {
            logicDeleteHelper.setLogicUser(invocation);
        }
        //SQL拦截解析封装数据权限
        String sql = MybatisUtils.getSqlByInvocation(invocation);
        if (isProceedImmediately(sql, sqlCommandType)) {
            return invocation.proceed();
        }
        return invocation.proceed();
    }

    boolean isProceedImmediately(String sql, SqlCommandType sqlCommandType) {
        if (!Properties.isEnableData()) {
            return true;
        }
        if (StringUtils.isBlank(sql)) {
            return true;
        }
        if (SqlCommandType.SELECT != sqlCommandType) {
            return true;
        }
        if (UserUtils.isEnableTenant()) {
            return false;
        }
        if (sql.contains(LT)) {
            return false;
        }
        return true;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }


    private void setDefaultValue(Invocation invocation, SqlCommandType sqlCommandType) {
        Object parameter = invocation.getArgs()[1];
        if (parameter instanceof Map) {
            for (Object o : ((Map) parameter).values()) {
                setUserAndTenant(sqlCommandType, o);
            }
        } else {
            setUserAndTenant(sqlCommandType, parameter);
        }
    }

    private void setUserAndTenant(SqlCommandType sqlCommandType, Object object) {
        if (object == null) {
            return;
        }
        String tenantCode = UserUtils.getCodeFromThreadLocal("tenantCode");
        String userCode = UserUtils.getCodeFromThreadLocal("userCode");
        String userName = UserUtils.getCodeFromThreadLocal("userName");
        if (SqlCommandType.INSERT == sqlCommandType) {
            if (object instanceof Map) {
                ((Map<String, Object>) object).put("tenantCode", tenantCode);
            }
            UserUtils.setFieldValue(object, "createrCode", userCode, true);
            UserUtils.setFieldValue(object, "updaterCode", userCode, true);
            UserUtils.setFieldValue(object, "tenantCode", tenantCode, true);
            UserUtils.setFieldValue(object, "createrName", userName, true);
            UserUtils.setFieldValue(object, "updaterName", userName, true);
        } else if (SqlCommandType.UPDATE == sqlCommandType) {
            UserUtils.setFieldValue(object, "updaterCode", userCode, true);
            UserUtils.setFieldValue(object, "updaterName", userName, true);
            UserUtils.setFieldValue(object, "tenantCode", tenantCode, true);
        } else if (SqlCommandType.SELECT == sqlCommandType && UserUtils.isEnableTenant()) {
            UserUtils.setFieldValue(object, "tenantCode", tenantCode, true);
        }
    }
}