在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/