前言
本文主要是講通過 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