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);
}
}
}