栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

自定义Mybatis自动填充插件----mybatis-plus 自动填充失效、分页插件不向下传递拦截链

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

自定义Mybatis自动填充插件----mybatis-plus 自动填充失效、分页插件不向下传递拦截链

实战业务优化方案总结—主目录https://blog.csdn.net/grd_java/article/details/124346685

问题如标题所示,mybatis自动填充不生效,使用分页插件发现这个插件没有向下传递拦截链。

解决方案

  1. 选择使用Mybatis-plus的分页插件,满足向下传递拦截链的要求。
  2. 仿造MyBatis-plus的自动填充使用,自定义自动填充插件。解决Mybatis-plus自动填充时而有效时而失效的情况。

自动填充效果展示



文章目录
  • 1. 分页插件
  • 2. 自定义mybatis
  • 3. 实现自动填充

1. 分页插件
1. 使用Mybatis-plus的分页插件,满足向下传递拦截链的要求
  1. 很简单,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,事务管理器等等
  1. 很简单,创建一个配置类配置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
@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
}
  1. 在需要自动填充的字段上,添加注解
  2. 编写自定义插件(我们选择拦截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;


//@Component //因为在DataSourceConfigOne,自定义了SqlSessionFactory,并指定拦截链,所以这里不需要用注解
@Intercepts({//可以定义多个@Singnature,对多个地方拦截,都用这个拦截器
        @Signature(type = ParameterHandler.class,//指定拦截ParameterHandler接口
                method = "setParameters",//指定要拦截这个接口(指定拦截ParameterHandler接口)的具体方法名,必须写对
                args={PreparedStatement.class}//拦截方法的参数列表,必须和你要拦截的方法一一对应
        )
})
//指定在某个类加载后再加载,防止其它的拦截器,比如分页拦截器不invocation.proceed()向下传递拦截链
//因为越往后的优先级越高,所以要在其它拦截器后面加载,这样它的优先级会更高
//@AutoConfigureAfter(PaginationInterceptor.class) //因为在DataSourceConfigOne,自定义了SqlSessionFactory,并指定拦截链,所以这里不需要
public class MyPluginAutoFill implements Interceptor {
    @Autowired
    private LoginUserUtil loginUserUtil;//用来获取当前登录的用户信息

    //insert操作时,需要填充的字段
    //只填充有@MyTableField(fill = MyFieldFill.INSERT),和@MyTableField(fill = MyFieldFill.INSERT_UPDATE)注解的字段
    //字段的自动填充,需要满足两个条件,在下面指定字段,并且在需要填充字段的地方,加上上面的注解,只满足其中一个条件,不会进行填充
    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);
    }
    //update操作时,需要填充的字段
    //只填充有@MyTableField(fill = MyFieldFill.UPDATE),和@MyTableField(fill = MyFieldFill.INSERT_UPDATE)注解的字段
    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 mybatisDefaultParameterHandler = realTarget(invocation.getTarget());
        //反射mappedStatement对象出来
        Field mappedStatementField = mybatisDefaultParameterHandler.getClass().getDeclaredField("mappedStatement");
        mappedStatementField.setAccessible(true);//此属性是private的,需要强制
        //获取MappedStatement
        MappedStatement mappedStatement =(MappedStatement)mappedStatementField.get(mybatisDefaultParameterHandler);
        // 如果不是insert或update,就不需要自动填充,直接传递拦截链
        if(mappedStatement.getSqlCommandType()!=SqlCommandType.INSERT && mappedStatement.getSqlCommandType()!=SqlCommandType.UPDATE)
            return invocation.proceed();//原方法执行,并返回

        // 获取parameterObject对象,用于设置参数
        Object parameterObject = realTarget(mybatisDefaultParameterHandler.getParameterObject());
        // 如果是insert或update,就执行对应的自动填充
        if(mappedStatement.getSqlCommandType()==SqlCommandType.INSERT){
            insertBefore(parameterObject);
        }else if (mappedStatement.getSqlCommandType()==SqlCommandType.UPDATE){
            updateBefore(parameterObject);
        }
        return invocation.proceed();//原方法执行,并返回,传递拦截链,不传递的话,后面的拦截器将无法获取
    }
    
    public static  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属性
        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);
            }
        }
    }

//        
//        @Override
//        public Object plugin(Object target) {
//                System.out.println("为"+target+"生成代理");
//                return Plugin.wrap(target,this);
//        }
//        //获取配置文件属性,xml中配置的属性,从Properties获取,插件初始化时调用。只调用一次,插件配置的属性从这里设置
//        @Override
//        public void setProperties(Properties properties) {
//                System.out.println("插件配置的初始化参数:"+properties);
//        }
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/825417.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号