场景:
项目里面有很多更新创建人,更新人的地方。每次新增,更新数据的时候都需要带入这些信息。
一般的处理逻辑:
每个sql语句都携带创建人,更新人信息,人员信息从线程本地变量中获取。
public class UserInfoThreadLocal{
private static ThreadLocal<Long> userIdLocal = new ThreadLocal<>();
public static Long getUserId(){
return userIdLocal.get();
}
}
每个业务sql都如下
insert into 表名(id,create_by) values(1,"张三")
更新时
update 表名 set 字段名 = #{name},update_by = #{userId} where id = #{id};
每个业务sql都需要带入更新人,创建人信息。
用拦截器实现通用的逻辑,每个业务sql不再需要关心这两个字段,这两个字段的设置交给拦截器去做。
示例代码如下:
@Intercepts(value = {@Signature(type = Executor.class,method = "update",args = {MappedStatement.class,Object.class})})
public class MybatisAutoSetUserIdInterceptor implements Interceptor{
//数据库字段对应的实体字段
private static final String createBy = "createBy";
//数据库字段对应的实体字段
private static final String updateBy = "updateBy";
public Object intercept(Invocation invocation) throws Throwable{
Long userId = UserInfoThreadLocal.getUser();
if(Objects.nonNull(userId)){
MappedStatement statement = (MappedStatement)invocation.getArgs()[0];
SqlCommandType sqlType = statement.getSqlCommandType();
Object paramter = invocation.getArgs()[1];
doSetUserIdWhenUpdate(sqlType,paramter,userId);
}
return invocation.proceed();
}
private void doSetUserIdWhenUpdate(SqlCommandType sqlType,Object paramter,Long userId){
if(parameter instanceof Map){
Map parameterMap = (Map)parameter;
//批量更新
if(parameterMap.containsKey("list")){
List list = (List) parameterMap.get("list");
list.forEach(e -> {
updateUserIdField(sqlType,e,userId);
});
}else{
//单个更新
updateUserIdField(sqlType,paramter,userId);
}
}
}
private void updateUserIdField(SqlCommandType commandType,Object obj,Long userId){
Field[] fields = obj.getClass().getDeclaredFields();
if(Objects.nonNull(obj.getClass().getSuperclass())){
Field[] superFields = obj.getClass().getSuperclass().getDeclaredFields();
fields = ArrayUtils.addAll(fields,superFields);
}
if(SqlCommandType.INSERT.equals(commandType)
|| SqlCommandType.UPDATE.equals(commandType)){
Arrays.stream(fields)
.filter(e -> Objects.equals(createBy,e.getName()) || Objects.equals(updateBy,e.getName()))
.forEach(e -> {
e.setAccessible(true);
e.set(obj,userId);
});
}
}
@Override
public Object plugin(Object o){
if(o instanceof Executor){
return Plugin.wrap(o,this);
}
return o;
}
@Override
public void setProperties(Properties properties){
}
}
拦截器注入到容器中
@Configuration
public class MyBatisConfiguration{
@Bean
public MybatisAutoSetUserIdInterceptor mybatisAutoSetUserIdInterceptor(){
return new MybatisAutoSetUserIdInterceptor();
}
}