Mybatis拦截实现查询sql统一处理

 

https://blog.csdn.net/e_anjing/article/details/79102693

1、实现拦截接口

Mybatis提供了拦截接口,可通过实现该接口,配合springMVC的配置,完成sql拦截。

 

 
  1. import java.util.Properties;

  2.  
  3. import org.apache.ibatis.executor.Executor;

  4. import org.apache.ibatis.mapping.MappedStatement;

  5. import org.apache.ibatis.plugin.Interceptor;

  6. import org.apache.ibatis.plugin.Intercepts;

  7. import org.apache.ibatis.plugin.Invocation;

  8. import org.apache.ibatis.plugin.Plugin;

  9. import org.apache.ibatis.plugin.Signature;

  10. import org.apache.ibatis.session.ResultHandler;

  11. import org.apache.ibatis.session.RowBounds;

  12.  
  13. @Intercepts(@Signature(type = Executor.class, method = "query",

  14. args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}))

  15. public class MybatisSqlInterceptor implements Interceptor {

  16.  
  17. @Override

  18. public Object intercept(Invocation invocation) throws Throwable {

  19. return invocation.proceed();

  20. }

  21.  
  22. @Override

  23. public Object plugin(Object obj) {

  24. return Plugin.wrap(obj, this);

  25. }

  26.  
  27. @Override

  28. public void setProperties(Properties arg0) {

  29. // doSomething

  30. }

  31. }

2、xml配置

找到mybatis的配置,在configuration处如下配置:

 

 

完成以上两步,则框架能对查询的sql进行拦截了。接下来,我们要在MybatisSqlInterceptor的intercept方法内实现sql的调整重置了。

 

1、获取sql

 

 
  1. private String getSqlByInvocation(Invocation invocation) {

  2. final Object[] args = invocation.getArgs();

  3. MappedStatement ms = (MappedStatement) args[0];

  4. Object parameterObject = args[1];

  5. BoundSql boundSql = ms.getBoundSql(parameterObject);

  6. return boundSql.getSql();

  7. }

2、通过获取的springMVC中的处理类的bean名称,得到实例和处理方法,循环处理sql

 

 
  1. private String processSqlByInterceptor(Invocation invocation, String sql, String[] interceptorNames) {

  2. if (interceptorNames == null || interceptorNames.length == 0) {

  3. return sql;

  4. }

  5.  
  6. String resultSql = sql;

  7. for (int i = 0; i < interceptorNames.length; i++) {

  8. Object interceptorObj = SpringContextUtil.getBean(interceptorNames[i]);//SpringContextUtil:spring获取bean的工具类

  9. if (interceptorObj == null) {

  10. continue;

  11. }

  12. resultSql = ((SqlInterceptor) interceptorObj).doInterceptor(invocation, resultSql);

  13. }

  14.  
  15. return resultSql;

  16. }

3、包装sql后重置sql

 

 
  1. private void resetSql2Invocation(Invocation invocation, String sql) throws SQLException {

  2. final Object[] args = invocation.getArgs();

  3. MappedStatement statement = (MappedStatement) args[0];

  4. Object parameterObject = args[1];

  5. BoundSql boundSql = statement.getBoundSql(parameterObject);

  6. MappedStatement newStatement = newMappedStatement(statement, new BoundSqlSqlSource(boundSql));

  7. MetaObject msObject = MetaObject.forObject(newStatement);

  8.  
  9. msObject.setValue("sqlSource.boundSql.sql", sql);

  10. args[0] = newStatement;

  11. }

 
  1. private MappedStatement newMappedStatement(MappedStatement ms, SqlSource newSqlSource) {

  2. MappedStatement.Builder builder =

  3. new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType());

  4. builder.resource(ms.getResource());

  5. builder.fetchSize(ms.getFetchSize());

  6. builder.statementType(ms.getStatementType());

  7. builder.keyGenerator(ms.getKeyGenerator());

  8. if (ms.getKeyProperties() != null && ms.getKeyProperties().length != 0) {

  9. StringBuilder keyProperties = new StringBuilder();

  10. for (String keyProperty : ms.getKeyProperties()) {

  11. keyProperties.append(keyProperty).append(",");

  12. }

  13. keyProperties.delete(keyProperties.length() - 1, keyProperties.length());

  14. builder.keyProperty(keyProperties.toString());

  15. }

  16. builder.timeout(ms.getTimeout());

  17. builder.parameterMap(ms.getParameterMap());

  18. builder.resultMaps(ms.getResultMaps());

  19. builder.resultSetType(ms.getResultSetType());

  20. builder.cache(ms.getCache());

  21. builder.flushCacheRequired(ms.isFlushCacheRequired());

  22. builder.useCache(ms.isUseCache());

  23.  
  24. return builder.build();

  25. }

定义一个内部辅助类,作用是包装sql

 

 
  1. class BoundSqlSqlSource implements SqlSource {

  2.  
  3.  
  4. private BoundSql boundSql;

  5.  
  6.  
  7. public BoundSqlSqlSource(BoundSql boundSql) {

  8. this.boundSql = boundSql;

  9. }

  10.  
  11.  
  12. @Override

  13. public BoundSql getBoundSql(Object parameterObject) {

  14. return boundSql;

  15. }

  16. }

4、辅助:获取操作类型

 

 
  1. private String getOperateType(Invocation invocation) {

  2. final Object[] args = invocation.getArgs();

  3. MappedStatement ms = (MappedStatement) args[0];

  4. SqlCommandType commondType = ms.getSqlCommandType();

  5. if (commondType.compareTo(SqlCommandType.SELECT) == 0) {

  6. return SQL_OPERATE_TYPE_SELECT;

  7. }

  8. if (commondType.compareTo(SqlCommandType.INSERT) == 0) {

  9. return SQL_OPERATE_TYPE_INSERT;

  10. }

  11. if (commondType.compareTo(SqlCommandType.UPDATE) == 0) {

  12. return SQL_OPERATE_TYPE_UPDATE;

  13. }

  14. if (commondType.compareTo(SqlCommandType.DELETE) == 0) {

  15. return SQL_OPERATE_TYPE_DELETE;

  16. }

  17. return null;

  18. }

5、辅助:获取properties中的常量配置

 

 
  1. private String[] getInterceptorNames(Invocation invocation) {

  2. String operateType = getOperateType(invocation);

  3. if (StringUtils.isBlank(operateType)) {

  4. return new String[] {};

  5. }

  6.  
  7. String interceptorNameKey = "sql.interceptor.name.4." + operateType;

  8. String interceptorNameString = CfgFactory.getDefault().getFirst(interceptorNameKey);

  9. if (StringUtils.isBlank(interceptorNameString)) {

  10. return new String[] {};

  11. }

  12.  
  13. return interceptorNameString.split(",");

  14. }

6、辅助:处理类接口

 

 
  1. import org.apache.ibatis.plugin.Invocation;

  2.  
  3. /**

  4. * sql处理类接口,提供sql处理方法,并将处理后的sql返回

  5. *

  6. * @版权:SINOSERVICES 版权所有 (c) 2013

  7. * @author:admin

  8. * @version Revision 1.0.0

  9. * @email:admin

  10. * @see:

  11. * @创建日期:2018年1月18日

  12. * @功能说明:

  13. * @begin

  14. * @修改记录:

  15. * @修改后版本 修改人 修改内容

  16. * @2018年1月18日 admin 创建

  17. * @end

  18. */

  19. public interface SqlInterceptor{

  20. /**

  21. * @param invocation

  22. * @param sql

  23. * @return

  24. * @author:admin

  25. * @email:admin

  26. * @创建日期:2018年1月10日

  27. * @功能说明: 拦截查询的接口方法,通过原始的sql,判断是否含有占位符。如果没有占位符,则直接返回。否则,根据数据权限查询得到数据权限的sql替换占位符后返回。

  28. */

  29. public String doInterceptor(Invocation invocation, String sql);

  30. }

以上的方法,提供了interceptor的功能支持。现在我们组合起来,改造interceptor方法。

 

 
  1. @Override

  2. public Object intercept(Invocation invocation) throws Throwable {

  3.  
  4. // 获取sql

  5. String sql = getSqlByInvocation(invocation);

  6. if (StringUtils.isBlank(sql)) {

  7. return invocation.proceed();

  8. }

  9.  
  10. // 获取业务系统中配置的sql处理器(service)的名称

  11. String[] interceptorNames = getInterceptorNames(invocation);

  12. if (interceptorNames == null || interceptorNames.length == 0) {

  13. return invocation.proceed();

  14. }

  15.  
  16. // sql交由处理类处理

  17. String sql2Reset = processSqlByInterceptor(invocation, sql, interceptorNames);

  18.  
  19. // 包装sql后,重置到invocation中

  20. resetSql2Invocation(invocation, sql2Reset);

  21.  
  22. // 返回,继续执行

  23. return invocation.proceed();

  24. }

这样,我们就实现了查询sql的统一调整了。业务系统内的所有查询,都将被此拦截器拦截,并做处理。

当然,处理的逻辑,因各业务而不一。

我们的MybatisSqlInterceptor,提供的是常量配置的地方配置好处理接口SqlInterceptor的实现类的bean名称

 

sql.interceptor.name.4.select=dataAuthInterceptor

再在业务系统中,实现该接口即可。

你可能感兴趣的:(java,mybatis)