mybatis数据权限自定义插件,在开发的过程中,我们常常会遇到在sql后面加上固定条件,尤其是租户的时候,这个时候我们又想动态的加上租户条件,这时候数据权限插件应运而生。
实现原理是注解加上mybatis插件,插件扫描xx方法是否包含xx注解,包含就进行sql拼接,拼接sql要注意语法问题,插件在xml的动态sql后执行。
1.DataScope注解
/**
* 数据权限注解
*
* @author glq
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataScope {
String tableAlis() default "";
}
2.DataScopeInterceptor数据权限插件
/**
* @author glq
**/
@Intercepts
({
@Signature(type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
)
})
public class DataScopeInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println(invocation.getMethod().getName());
Object[] queryArgs = invocation.getArgs();
MappedStatement mappedStatement = (MappedStatement) queryArgs[0];
BoundSql boundSql = mappedStatement.getBoundSql(queryArgs[1]);
String queryId = mappedStatement.getId();
int index = queryId.lastIndexOf(".");
String className = queryId.substring(0, index);
String methodName = queryId.substring(index + 1);
System.out.println(queryId);
Class clazz = Class.forName(className);
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
if (method.getName().equals(methodName)) {
DataScope dataScope = AnnotationUtil.getAnnotation(method, DataScope.class);
if (dataScope != null) {
System.err.println("拦截到指定方法");
//测试组装sql
StringBuilder builder = new StringBuilder(boundSql.getSql());
builder.append(" 1=1");
// 重新new一个查询语句对象
BoundSql newBoundSql = new BoundSql(mappedStatement.getConfiguration(),builder.toString(),
boundSql.getParameterMappings(), boundSql.getParameterObject());
// 把新的查询放到statement里
MappedStatement newMs = newMappedStatement(mappedStatement, new BoundSqlSqlSource(newBoundSql));
for (ParameterMapping mapping : boundSql.getParameterMappings()) {
String prop = mapping.getProperty();
if (boundSql.hasAdditionalParameter(prop)) {
newBoundSql.setAdditionalParameter(prop, boundSql.getAdditionalParameter(prop));
}
}
queryArgs[0] = newMs;
return invocation.proceed();
}
}
}
return invocation.proceed();
}
/**
* 定义一个内部辅助类,作用是包装 SQL
*/
class BoundSqlSqlSource implements SqlSource {
private BoundSql boundSql;
public BoundSqlSqlSource(BoundSql boundSql) {
this.boundSql = boundSql;
}
@Override
public BoundSql getBoundSql(Object parameterObject) {
return boundSql;
}
}
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) {
builder.keyProperty(ms.getKeyProperties()[0]);
}
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();
}
}
最后别忘了把插件注入spring容器!