Springboot2(22)Mybatis攔截器實現
阿新 • • 發佈:2018-12-29
MyBatis提供了一種外掛(plugin)的功能,雖然叫做外掛,但其實這是攔截器功能
MyBatis 允許攔截的介面
MyBatis 允許你在已對映語句執行過程中的某一點進行攔截呼叫。預設情況下,MyBatis 允許使用外掛來攔截的方法呼叫包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
Executor介面的部分方法,比如update,query,commit,rollback等方法,還有其他介面的一些方法等。
總體概括為:
- 攔截執行器的方法
- 攔截引數的處理
- 攔截結果集的處理,為sql執行之後的結果攔截過濾
- 攔截Sql語法構建的處理,為sql執行之前的攔截進行sql封裝
MyBatis攔截器的介面定義
一共有三個方法intercept
、plugin
、setProperties
setProperties()
方法主要是用來從配置中獲取屬性。
如果是使用xml式配置攔截器,可在Mybatis配置檔案中新增如下節點,屬性可以以如下方式傳遞
<plugins> <plugin interceptor="tk.mybatis.simple.plugin.XXXInterceptor"> <property name="propl" value="valuel" /> <property name="prop2" value="value2" /> </plugin> </plugins>
如果在Spring boot
中使用,則需要單獨寫一個配置類,如下:
@Configuration
public class MybatisInterceptorConfig {
@Bean
public String myInterceptor(SqlSessionFactory sqlSessionFactory) {
ExecutorInterceptor executorInterceptor = new ExecutorInterceptor();
Properties properties = new Properties();
properties.setProperty("prop1","value1");
executorInterceptor.setProperties(properties);
return "interceptor";
}
}
如果說不需要配置屬性,則在spring boot
中,不需要去編寫配置類,只需要像我一樣在攔截器上加個@Component
即可。
plugin()
方法用於指定哪些方法可以被此攔截器攔截。
intercept()
方法是用來對攔截的sql
進行具體的操作。
註解實現
MyBatis
攔截器用到了兩個註解:@Intercepts
和@Signature
@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}),
}
)
type
的值與類名相同,method
與方法名相同,為了避免方法過載,args
中指定了各個引數的型別和個數,可通過invocation.getArgs()
獲取引數陣列。
Executor 攔截器實現
@Intercepts({
@Signature(
type= Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
)
})
@Slf4j
@Component
public class ExecutorInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
String sql = ExecutorPluginUtils.getSqlByInvocation(invocation);
//可以對sql重寫
log.error("攔截器ExecutorInterceptor:"+sql);
//sql = "SELECT id from BUS_RECEIVER where id = ? ";
ExecutorPluginUtils.resetSql2Invocation( invocation, sql);
return invocation.proceed();
}
@Override
public Object plugin(Object o) {
return Plugin.wrap(o, this);
}
@Override
public void setProperties(Properties properties) {
}
}
ParameterHandler 攔截器實現
/**
* @Author: ynz
* @Date: 2018/12/23/023 12:07
*/
@Intercepts({
@Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),
})
@Component
@Slf4j
public class ParamInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
log.error("攔截器ParamInterceptor");
//攔截 ParameterHandler 的 setParameters 方法 動態設定引數
if (invocation.getTarget() instanceof ParameterHandler) {
ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
PreparedStatement ps = (PreparedStatement) invocation.getArgs()[0];
// 反射獲取 BoundSql 物件,此物件包含生成的sql和sql的引數map對映
Field boundSqlField = parameterHandler.getClass().getDeclaredField("boundSql");
boundSqlField.setAccessible(true);
BoundSql boundSql = (BoundSql) boundSqlField.get(parameterHandler);
// 反射獲取 引數對像
Field parameterField =
parameterHandler.getClass().getDeclaredField("parameterObject");
parameterField.setAccessible(true);
Object parameterObject = parameterField.get(parameterHandler);
if (parameterObject instanceof Map) {
//將引數中的name值改為2
((Map) parameterObject).put("name","2");
}
// 改寫的引數設定到原parameterHandler物件
parameterField.set(parameterHandler, parameterObject);
parameterHandler.setParameters(ps);
log.error(JSON.toJSONString(boundSql.getParameterMappings()));
log.error(JSON.toJSONString(parameterObject));
}
return invocation.proceed();
}
@Override
public Object plugin(Object o) {
return Plugin.wrap(o, this);
}
@Override
public void setProperties(Properties properties) {
}
}
ResultSetHandler 攔截器實現
@Intercepts({
@Signature(type = ResultSetHandler.class, method = "handleResultSets", args={Statement.class})
})
@Component
@Slf4j
public class ResultInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
log.error("攔截器ResultInterceptor");
// ResultSetHandler resultSetHandler1 = (ResultSetHandler) invocation.getTarget();
//通過java反射獲得mappedStatement屬性值
//可以獲得mybatis裡的resultype
Object result = invocation.proceed();
if (result instanceof ArrayList) {
ArrayList resultList = (ArrayList) result;
for (int i = 0; i < resultList.size(); i++) {
Object oi = resultList.get(i);
Class c = oi.getClass();
Class[] types = {String.class};
Method method = c.getMethod("setAddress", types);
// 呼叫obj物件的 method 方法
method.invoke(oi, "china");
}
}
return result;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
StatementHandler 攔截器實現
/**
* @Author: ynz
* @Date: 2018/12/23/023 14:22
*/
@Intercepts(
{@Signature(
type = StatementHandler.class,
method = "prepare",
args = {Connection.class, Integer.class}
)
})
@Component
@Slf4j
public class StatementInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler =
(StatementHandler) PluginUtils.realTarget(invocation.getTarget());
MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
MappedStatement mappedStatement =
(MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");
//只攔截select方法
if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {
return invocation.proceed();
}
BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");
//獲取到sql
String originalSql = boundSql.getSql();
//可以對originalSql進行改寫
log.error("攔截器StatementInterceptor:"+originalSql);
metaStatementHandler.setValue("delegate.boundSql.sql", originalSql);
Object parameterObject = boundSql.getParameterObject();
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
Spring Boot整合
方法1:手寫一個配置類
@Configuration
public class MybatisInterceptorConfig {
@Bean
public String myInterceptor(SqlSessionFactory sqlSessionFactory) {
ExecutorInterceptor executorInterceptor = new ExecutorInterceptor();
Properties properties = new Properties();
properties.setProperty("prop1","value1");
executorInterceptor.setProperties(properties);
sqlSessionFactory.getConfiguration().addInterceptor(executorInterceptor);
sqlSessionFactory.getConfiguration().addInterceptor(new ParamInterceptor());
sqlSessionFactory.getConfiguration().addInterceptor(new ResultInterceptor());
return "interceptor";
}
}