- mybatis-plus逻辑删除设置
在po里面增加注解:
/**
* 状态,0可用,1不可用
*/
@TableLogic(value = "0", delval = "1")
private Integer deleted
- mybatis-plus拦截器配置
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
- 配置文件
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
- 编写拦截器方法
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
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())
}
}
- 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);
}
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);
}
}
}