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/

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章