• MyBatis拦截器:给参数对象属性赋值


    在mybatis的mapper.xml文件中,我们可以使用#{}或${}的方式获取到参数,这些参数都需要提前我们在mapper.java接口文件中通过参数的方式传入参数才能取到

    为了扩展参数,我们需要了解mybatis是怎么帮我们保管mapper.java中传入的参数的

    进入Executor.java接口查看query方法,可以看到第一个参数MappedStatement对象中有一个parameterMap字段,该字段是Map类型保存我们的参数,那我们只需要在拦截器中对MappedStatement对象的parameterMap中put自己想要的参数即可

    @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})
    })
    public class DeptDataScopeInterceptor implements Interceptor {
    
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            Object[] args = invocation.getArgs();
    
            if (ArrayUtils.isNotEmpty(args)) {
                MappedStatement mappedStatement = (MappedStatement) args[0];
                DataScope dataScope = getDataScope(mappedStatement);
    
                StringBuilder sqlString = new StringBuilder();
                if (dataScope != null) {
                    Long companyOrgId = SecurityUtils.getCompanyOrgId();
    
                    if (companyOrgId != null) {
                        sqlString.append(StringUtils.format(" {}.{} = {} ", dataScope.orgAlias(), dataScope.orgColumnName(), companyOrgId));
                    } else {
                        sqlString.append(" 1=0 ");
                    }
                }
    
                if (args[1] == null) {
                    args[1] = new MapperMethod.ParamMap<>();
                }
                Map map = (Map) args[1];
                // 此处为重点
                map.put("dataScope", sqlString.length() > 0 ? " (" + sqlString.toString() + ")" : "");
            }
            return invocation.proceed();
        }
    
        private DataScope getDataScope(MappedStatement mappedStatement) {
            String id = mappedStatement.getId();
            // 获取 Class Method
            String clazzName = id.substring(0, id.lastIndexOf('.'));
            String mapperMethod = id.substring(id.lastIndexOf('.') + 1);
    
            Class<?> clazz;
            try {
                clazz = Class.forName(clazzName);
            } catch (ClassNotFoundException e) {
                return null;
            }
            Method[] methods = clazz.getMethods();
    
            DataScope dataScope = null;
            for (Method method : methods) {
                if (method.getName().equals(mapperMethod)) {
                    dataScope = method.getAnnotation(DataScope.class);
                    break;
                }
            }
            return dataScope;
        }
    
        @Override
        public Object plugin(Object target) {
            return Plugin.wrap(target, this);
        }
    
        @Override
        public void setProperties(Properties properties) {
    
        }
    }

    在map中加入dataScope后,即可在相应的mapper.xml中使用#{dataScope}或${dataScope}或取到该参数

    如果项目中使用了pageHelper插件,则启动项目后,执行到对应的mapper查询的时候,如果mapper.xml中使用了上述方式添加的参数,那么项目会报错,因为pageHelper的拦截器会在我们的拦截器之前执行,pageHelper的拦截器中对参数进行了校验,因为自定的拦截器还没有执行,则Map中不会有自定义参数,当pageHelper的拦截器开始校验参数的时候就会报错找不到参数

    如何将自定义的拦截器放在PageHelper拦截器前方执行
    查阅资料后发现SqlSessionFactory中加入的拦截器,先加入的会后执行,后加入的先执行,那么我们需要让自定义的拦截器在PageHelper后加入,查看PageHelper的自动配置类

    @Configuration
    @ConditionalOnBean(SqlSessionFactory.class)
    @EnableConfigurationProperties(PageHelperProperties.class)
    @AutoConfigureAfter(MybatisAutoConfiguration.class)
    @Lazy(false)
    public class PageHelperAutoConfiguration {
    
        @Autowired
        private List<SqlSessionFactory> sqlSessionFactoryList;
    
        @Autowired
        private PageHelperProperties properties;
    
        /**
         * 接受分页插件额外的属性
         *
         * @return
         */
        @Bean
        @ConfigurationProperties(prefix = PageHelperProperties.PAGEHELPER_PREFIX)
        public Properties pageHelperProperties() {
            return new Properties();
        }
    
        @PostConstruct
        public void addPageInterceptor() {
            PageInterceptor interceptor = new PageInterceptor();
            Properties properties = new Properties();
            //先把一般方式配置的属性放进去
            properties.putAll(pageHelperProperties());
            //在把特殊配置放进去,由于close-conn 利用上面方式时,属性名就是 close-conn 而不是 closeConn,所以需要额外的一步
            properties.putAll(this.properties.getProperties());
            interceptor.setProperties(properties);
            for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
                org.apache.ibatis.session.Configuration configuration = sqlSessionFactory.getConfiguration();
                if (!containsInterceptor(configuration, interceptor)) {
                    configuration.addInterceptor(interceptor);
                }
            }
        }
    
        /**
         * 是否已经存在相同的拦截器
         *
         * @param configuration
         * @param interceptor
         * @return
         */
        private boolean containsInterceptor(org.apache.ibatis.session.Configuration configuration, Interceptor interceptor) {
            try {
                // getInterceptors since 3.2.2
                return configuration.getInterceptors().contains(interceptor);
            } catch (Exception e) {
                return false;
            }
        }
    
    }

    pageHelper的拦截器会在Mybatis的自动配置后在做相应配置,那么我们可以写一个配置类,让他在PageHelper的配置类后再执行

    @Configuration
    @ConditionalOnBean(SqlSessionFactory.class)
    @EnableConfigurationProperties(PageHelperProperties.class)
    @Lazy(false)
    @AutoConfigureAfter(PageHelperAutoConfiguration.class)
    public class DeptDataScopeInterceptorConfig {
    
        @Autowired
        private List<SqlSessionFactory> sqlSessionFactoryList;
    
        @PostConstruct
        public void addPageInterceptor() {
            DeptDataScopeInterceptor interceptor = new DeptDataScopeInterceptor();
            for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
                sqlSessionFactory.getConfiguration().addInterceptor(interceptor);
            }
        }
    
    }

    此处需要注意的是,如果DeptDataScopeInterceptorConfig配置类会被SpringBoot项目自动扫描到,尽管配置的顺序是在PageHelper自动配置后再配置,但是被SpringBoot扫描到的配置类会优先加载,所以要防止SpringBoot扫描带该配置,有如下几种方案

    如果该配置类已经会被扫描到,则可以使用排出的方式排除掉该类,然后用spring.factories的方式做自动配置,可以使用

    1.@SpringBootApplication(exclude = DeptDataScopeInterceptorConfig.class)

    2.@ComponentScan(excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = DeptDataScopeInterceptorConfig.class))

    3.添加到spring.factories中文件路径地址resrouces/META-INF/spring.factories(如果报错,可以试试在加上@EnableAutoConfiguration(exclude = DeptDataScopeInterceptorConfig.class))

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=
      top.sclf.common.datascope.config.DeptDataScopeInterceptorConfig

    参考

    https://blog.csdn.net/qq1010830256/article/details/115617442

    http://xtong.tech/2018/08/01/MyBatis%E6%8B%A6%E6%88%AA%E5%99%A8%E5%9B%A0pagehelper%E8%80%8C%E5%A4%B1%E6%95%88%E7%9A%84%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3/

     

  • 相关阅读:
    基于百度翻译API开发属于自己的翻译工具
    .NET轻量级MVC框架:Nancy入门教程(二)——Nancy和MVC的简单对比
    基于uploadify.js实现多文件上传和上传进度条的显示
    浅析Ajax跨域原理及JQuery中的实现分析
    认识SQLServer索引以及单列索引和多列索引的不同
    用VS添加引用dll也会出错?你遇到过吗?
    .NET轻量级MVC框架:Nancy入门教程(一)——初识Nancy
    免费打造自己的个人网站,免费域名、免费空间、FTP、数据库什么的,一个不能少,没钱,也可以这么任性
    【30集iCore3_ADP出厂源代码(ARM部分)讲解视频】30-6底层驱动之多路开关选择器
    【6集iCore3_ADP触摸屏驱动讲解视频】6-4 底层驱动之SDRAM读写(上)
  • 原文地址:https://www.cnblogs.com/grasp/p/15157776.html
Copyright © 2020-2023  润新知