您的位置:首页 > 数据库

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

2018-01-19 09:39 627 查看
1、实现拦截接口
Mybatis提供了拦截接口,可通过实现该接口,配合springMVC的配置,完成sql拦截。
import java.util.Properties;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

@Intercepts(@Signature(type = Executor.class, method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}))
public class MybatisSqlInterceptor implements Interceptor {

@Override
public Object intercept(Invocation invocation) throws Throwable {
return invocation.proceed();
}

@Override
public Object plugin(Object obj) {
return Plugin.wrap(obj, this);
}

@Override
public void setProperties(Properties arg0) {
// doSomething
}
}2、xml配置 找到mybatis的配置,在configuration处如下配置:
<configuration>
<plugins>
<plugin interceptor="com.sinoservices.xframework.core.ibatis.SccSqlHandler"/>
</plugins>
</configuration>

完成以上两步,则框架能对查询的sql进行拦截了。接下来,我们要在MybatisSqlInterceptor的intercept方法内实现sql的调整重置了。
1、获取sql
private String getSqlByInvocation(Invocation invocation) {
final Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement) args[0];
Object parameterObject = args[1];
BoundSql boundSql = ms.getBoundSql(parameterObject);
return boundSql.getSql();
}2、通过获取的springMVC中的处理类的bean名称,得到实例和处理方法,循环处理sql private String processSqlByInterceptor(Invocation invocation, String sql, String[] interceptorNames) {
if (interceptorNames == null || interceptorNames.length == 0) {
return sql;
}

String resultSql = sql;
for (int i = 0; i < interceptorNames.length; i++) {
Object interceptorObj = SpringContextUtil.getBean(interceptorNames[i]);//SpringContextUtil:spring获取bean的工具类
if (interceptorObj == null) {
continue;
}
resultSql = ((SqlInterceptor) interceptorObj).doInterceptor(invocation, resultSql);
}

return resultSql;
}3、包装sql后重置sql private void resetSql2Invocation(Invocation invocation, String sql) throws SQLException {
final Object[] args = invocation.getArgs();
MappedStatement statement = (MappedStatement) args[0];
Object parameterObject = args[1];
BoundSql boundSql = statement.getBoundSql(parameterObject);
MappedStatement newStatement = newMappedStatement(statement, new BoundSqlSqlSource(boundSql));
MetaObject msObject = MetaObject.forObject(newStatement);

msObject.setValue("sqlSource.boundSql.sql", sql);
args[0] = newStatement;
}
private MappedStatement newMappedStatement(MappedStatement ms, SqlSource newSqlSource) {
MappedStatement.Builder builder =
new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType());
builder.resource(ms.getResource());
builder.fetchSize(ms.getFetchSize());
builder.statementType(ms.getStatementType());
builder.keyGenerator(ms.getKeyGenerator());
if (ms.getKeyProperties() != null && ms.getKeyProperties().length != 0) {
StringBuilder keyProperties = new StringBuilder();
for (String keyProperty : ms.getKeyProperties()) {
keyProperties.append(keyProperty).append(",");
}
keyProperties.delete(keyProperties.length() - 1, keyProperties.length());
builder.keyProperty(keyProperties.toString());
}
builder.timeout(ms.getTimeout());
builder.parameterMap(ms.getParameterMap());
builder.resultMaps(ms.getResultMaps());
builder.resultSetType(ms.getResultSetType());
builder.cache(ms.getCache());
builder.flushCacheRequired(ms.isFlushCacheRequired());
builder.useCache(ms.isUseCache());

return builder.build();
}
定义一个内部辅助类,作用是包装sql class BoundSqlSqlSource implements SqlSource {

private BoundSql boundSql;

public BoundSqlSqlSource(BoundSql boundSql) {
this.boundSql = boundSql;
}

@Override
public BoundSql getBoundSql(Object parameterObject) {
return boundSql;
}
}4、辅助:获取操作类型 private String getOperateType(Invocation invocation) {
final Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement) args[0];
SqlCommandType commondType = ms.getSqlCommandType();
if (commondType.compareTo(SqlCommandType.SELECT) == 0) {
return SQL_OPERATE_TYPE_SELECT;
}
if (commondType.compareTo(SqlCommandType.INSERT) == 0) {
return SQL_OPERATE_TYPE_INSERT;
}
if (commondType.compareTo(SqlCommandType.UPDATE) == 0) {
return SQL_OPERATE_TYPE_UPDATE;
}
if (commondType.compareTo(SqlCommandType.DELETE) == 0) {
return SQL_OPERATE_TYPE_DELETE;
}
return null;
}5、辅助:获取properties中的常量配置 private String[] getInterceptorNames(Invocation invocation) {
String operateType = getOperateType(invocation);
if (StringUtils.isBlank(operateType)) {
return new String[] {};
}

String interceptorNameKey = "sql.interceptor.name.4." + operateType;
String interceptorNameString = CfgFactory.getDefault().getFirst(interceptorNameKey);
if (StringUtils.isBlank(interceptorNameString)) {
return new String[] {};
}

return interceptorNameString.split(",");
}6、辅助:处理类接口
import org.apache.ibatis.plugin.Invocation;

/**
* sql处理类接口,提供sql处理方法,并将处理后的sql返回
*
* @版权:SINOSERVICES 版权所有 (c) 2013
* @author:admin
* @version Revision 1.0.0
* @email:admin
* @see:
* @创建日期:2018年1月18日
* @功能说明:
* @begin
* @修改记录:
* @修改后版本 修改人 修改内容
* @2018年1月18日 admin 创建
* @end
*/
public interface SqlInterceptor{
/**
* @param invocation
* @param sql
* @return
* @author:admin
* @email:admin
* @创建日期:2018年1月10日
* @功能说明: 拦截查询的接口方法,通过原始的sql,判断是否含有占位符。如果没有占位符,则直接返回。否则,根据数据权限查询得到数据权限的sql替换占位符后返回。
*/
public String doInterceptor(Invocation invocation, String sql);
}
以上的方法,提供了interceptor的功能支持。现在我们组合起来,改造interceptor方法。 @Override
public Object intercept(Invocation invocation) throws Throwable {

// 获取sql
String sql = getSqlByInvocation(invocation);
if (StringUtils.isBlank(sql)) {
return invocation.proceed();
}

// 获取业务系统中配置的sql处理器(service)的名称
String[] interceptorNames = getInterceptorNames(invocation);
if (interceptorNames == null || interceptorNames.length == 0) {
return invocation.proceed();
}

// sql交由处理类处理
String sql2Reset = processSqlByInterceptor(invocation, sql, interceptorNames);

// 包装sql后,重置到invocation中
resetSql2Invocation(invocation, sql2Reset);

// 返回,继续执行
return invocation.proceed();
} 这样,我们就实现了查询sql的统一调整了。业务系统内的所有查询,都将被此拦截器拦截,并做处理。 当然,处理的逻辑,因各业务而不一。
我们的MybatisSqlInterceptor,提供的是常量配置的地方配置好处理接口SqlInterceptor的实现类的bean名称
sql.interceptor.name.4.select=dataAuthInterceptor 再在业务系统中,实现该接口即可。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐