问题如标题所示,mybatis自动填充不生效,使用分页插件发现这个插件没有向下传递拦截链。
解决方案
- 选择使用Mybatis-plus的分页插件,满足向下传递拦截链的要求。
- 仿造MyBatis-plus的自动填充使用,自定义自动填充插件。解决Mybatis-plus自动填充时而有效时而失效的情况。
自动填充效果展示




1. 分页插件
1. 使用Mybatis-plus的分页插件,满足向下传递拦截链的要求 |
---|
- 很简单,MyBatis配置文件中,配置一下

import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableTransactionManagement
public class MyBatisConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
2. 自定义mybatis
2. 这里推荐,自己决定使用哪些插件,包括配置DataSource,事务管理器等等 |
---|
- 很简单,创建一个配置类配置SqlSession就可以了(这个对象保存了MyBatis很多东西,其中拦截器就在这里面)

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
@Configuration
@MapperScan(basePackages = "cn.xx.mapper",sqlSessionTemplateRef = "sqlOneSqlSessionTemplate")
public class DataSourceConfigOne {
@Autowired
private MyBatisConfig myBatisConfig;
@Bean(name = "sqlOneDataSource")
@ConfigurationProperties(prefix = "spring.datasource.sqlone")
@Primary
public DataSource sqlOne1DataSource(){
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "sqlOneSqlSessionFactory")
@Primary
public SqlSessionFactory sqlOneSql1SessionFactory(@Qualifier("sqlOneDataSource") DataSource dataSource) throws Exception{
MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/sqlone/*.xml"));
bean.setPlugins(myBatisConfig.paginationInterceptor(),myBatisConfig.myPluginAutoFill());
return bean.getObject();
}
@Bean(name = "sqlOneTransactionManager")
@Primary
public DataSourceTransactionManager sqlOne1TransactionManager(@Qualifier("sqlOneDataSource") DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "sqlOneSqlSessionTemplate")
@Primary
public SqlSessionTemplate sqlOne1SqlSessionTemplate(@Qualifier("sqlOneSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception{
return new SqlSessionTemplate(sqlSessionFactory);
}
}
3. 实现自动填充
3. 自定义自动填充插件(仿照mybatis-plus的自动填充),需要对MyBatis源码有深入了解。 |
---|
- 注解用来标识字段是否需要自动填充,Fill标识字段在哪些情况下被填充


import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface MyTableField {
String value() default "";
MyFieldFill fill() default MyFieldFill.DEFAULT;
}
public enum MyFieldFill {
DEFAULT,
INSERT,
UPDATE,
INSERT_UPDATE
}
- 在需要自动填充的字段上,添加注解

- 编写自定义插件(我们选择拦截ParameterHandler的setParameters方法),拦截后,会对INSERT和UPDATE操作的parameterObject 进行设置值,就达到自动填充的效果了。

import com.baomidou.mybatisplus.core.MybatisDefaultParameterHandler;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.beans.factory.annotation.Autowired;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.sql.PreparedStatement;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Objects;
@Intercepts({
@Signature(type = ParameterHandler.class,
method = "setParameters",
args={PreparedStatement.class}
)
})
public class MyPluginAutoFill implements Interceptor {
@Autowired
private LoginUserUtil loginUserUtil;
private void insertBefore(Object o) throws NoSuchFieldException, IllegalAccessException {
this.doBefore("createTime",MyFieldFill.INSERT,new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()),o);
this.doBefore("updateTime",MyFieldFill.INSERT,new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()),o);
this.doBefore("createUser",MyFieldFill.INSERT,loginUserUtil.getCurrentAdmin().getUsername(),o);
this.doBefore("updateUser",MyFieldFill.INSERT,loginUserUtil.getCurrentAdmin().getUsername(),o);
}
private void updateBefore(Object o) throws NoSuchFieldException, IllegalAccessException {
this.doBefore("updateTime",MyFieldFill.UPDATE,new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()),o);
this.doBefore("updateUser",MyFieldFill.UPDATE,loginUserUtil.getCurrentAdmin().getUsername(),o);
}
@Override
public Object intercept(Invocation invocation) throws Throwable {
MybatisDefaultParameterHandler mybatisDefaultParameterHandler = realTarget(invocation.getTarget());
Field mappedStatementField = mybatisDefaultParameterHandler.getClass().getDeclaredField("mappedStatement");
mappedStatementField.setAccessible(true);
MappedStatement mappedStatement =(MappedStatement)mappedStatementField.get(mybatisDefaultParameterHandler);
if(mappedStatement.getSqlCommandType()!=SqlCommandType.INSERT && mappedStatement.getSqlCommandType()!=SqlCommandType.UPDATE)
return invocation.proceed();
Object parameterObject = realTarget(mybatisDefaultParameterHandler.getParameterObject());
if(mappedStatement.getSqlCommandType()==SqlCommandType.INSERT){
insertBefore(parameterObject);
}else if (mappedStatement.getSqlCommandType()==SqlCommandType.UPDATE){
updateBefore(parameterObject);
}
return invocation.proceed();
}
public static <T> T realTarget(Object target) {
if (Proxy.isProxyClass(target.getClass())) {
MetaObject metaObject = SystemMetaObject.forObject(target);
return realTarget(metaObject.getValue("h.target"));
}
return (T) target;
}
private void doBefore(String fieldName,MyFieldFill operationType,Object fieldVal,Object metaObject) throws NoSuchFieldException, IllegalAccessException {
Field declaredField = metaObject.getClass().getDeclaredField(fieldName);
if (Objects.nonNull(fieldVal) && declaredField!=null && declaredField.isAnnotationPresent(MyTableField.class)) {
MyTableField annotation = declaredField.getAnnotation(MyTableField.class);
if (annotation.fill() == MyFieldFill.INSERT_UPDATE||
annotation.fill()==operationType){
declaredField.setAccessible(true);
declaredField.set(metaObject,fieldVal);
}
}
}
}