前言
本文主要是讲通过 MyBaits 的 Interceptor 的拓展点进行对 MyBatis 执行 SQL 之前做一个逻辑拦截实现自定义逻辑的插入执行。
适合场景:1. 比如限制数据库查询最大访问条数;2. 限制登录用户只能访问当前机构数据。
定义是否开启注解
定义是否开启注解, 主要做的一件事情就是是否添加 SQL 拦截器。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// 全局开启
@Retention (RetentionPolicy.RUNTIME)
@Target (ElementType.TYPE)
@Documented
@Import (MyBatisSqlInterceptorConfiguration. class )
public @interface EnableSqlInterceptor {
}
// 自定义注解
@Target ({ElementType.METHOD })
@Retention (RetentionPolicy.RUNTIME)
public @interface DataScope {
}
|
注册SQL 拦截器
注册一个 SQL 拦截器,会对符合条件的 SQL 查询操作进行拦截。
1
2
3
4
5
6
7
8
|
public class MyBatisSqlInterceptorConfiguration implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SqlSessionFactory sqlSessionFactory = applicationContext.getBean(SqlSessionFactory. class );
sqlSessionFactory.getConfiguration().addInterceptor( new MyBatisInterceptor());
}
}
|
处理逻辑
在处理逻辑中,我主要是做一个简单的 limit 1 案例,如果是自己需要做其他的逻辑需要修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
@Slf4j
@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 MyBatisInterceptor implements Interceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(MyBatisInterceptor. class );
@Override
public Object intercept(Invocation invocation) throws Throwable {
// TODO Auto-generated method stub
Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement) args[ 0 ];
Object parameter = args[ 1 ];
RowBounds rowBounds = (RowBounds) args[ 2 ];
ResultHandler resultHandler = (ResultHandler) args[ 3 ];
Executor executor = (Executor) invocation.getTarget();
CacheKey cacheKey;
BoundSql boundSql;
//由于逻辑关系,只会进入一次
if (args.length == 4 ) {
//4 个参数时
boundSql = ms.getBoundSql(parameter);
cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
} else {
//6 个参数时
cacheKey = (CacheKey) args[ 4 ];
boundSql = (BoundSql) args[ 5 ];
}
DataScope dataScope = getDataScope(ms);
if (Objects.nonNull(dataScope)) {
String origSql = boundSql.getSql();
log.info( "origSql : {}" , origSql);
// 组装新的 sql
// todo you weaving business
String newSql = origSql + " limit 1" ;
// 重新new一个查询语句对象
BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), newSql,
boundSql.getParameterMappings(), boundSql.getParameterObject());
// 把新的查询放到statement里
MappedStatement newMs = newMappedStatement(ms, new BoundSqlSource(newBoundSql));
for (ParameterMapping mapping : boundSql.getParameterMappings()) {
String prop = mapping.getProperty();
if (boundSql.hasAdditionalParameter(prop)) {
newBoundSql.setAdditionalParameter(prop, boundSql.getAdditionalParameter(prop));
}
}
args[ 0 ] = newMs;
if (args.length == 6 ) {
args[ 5 ] = newMs.getBoundSql(parameter);
}
}
LOGGER.info( "mybatis intercept sql:{},Mapper方法是:{}" , boundSql.getSql(), ms.getId());
return invocation.proceed();
}
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();
}
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) {
// TODO Auto-generated method stub
LOGGER.info( "MysqlInterCeptor plugin>>>>>>>{}" , target);
return Plugin.wrap(target, this );
}
@Override
public void setProperties(Properties properties) {
// TODO Auto-generated method stub
String dialect = properties.getProperty( "dialect" );
LOGGER.info( "mybatis intercept dialect:>>>>>>>{}" , dialect);
}
/**
* 定义一个内部辅助类,作用是包装 SQL
*/
class BoundSqlSource implements SqlSource {
private BoundSql boundSql;
public BoundSqlSource(BoundSql boundSql) {
this .boundSql = boundSql;
}
public BoundSql getBoundSql(Object parameterObject) {
return boundSql;
}
}
}
|
如何使用
我们在 XXXMapper 中对应的数据操作方法只要加入 @DataScope 注解即可。
1
2
3
4
5
6
7
8
|
@Mapper
public interface OrderMapper {
@Select ( "select 1 " )
@DataScope
Intger selectOne();
}
|
参考资料
总结
到此这篇关于MyBatis自定义SQL拦截器的文章就介绍到这了,更多相关MyBatis自定义SQL拦截器内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!
原文链接:https://juejin.cn/post/7021508758777888799