前言
Mybatis拦截器注解的基本写法格式为:
1 2 3
| @Intercepts({ @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}) })
|
说明:
@Intercepts
:标识该类是一个拦截器
@Signature
:拦截器相关属性设置
type
拦截器的类型,总共有4种,按照执行的先后顺序为:
Executor
:拦截执行器的方法。
ParameterHandler
:拦截参数的处理。
ResultHandler
:拦截结果集的处理。
StatementHandler
:拦截Sql语法构建的处理,绝大部分我们是在这里设置我们的拦截器。
method
可被拦截的方法,按照拦截器的不同类型,总共有下面这些方法:
拦截的类型 |
拦截的方法 |
Executor |
update, query, flushStatements, commit, rollback,getTransaction, close, isClosed |
ParameterHandler |
getParameterObject, setParameters |
StatementHandler |
prepare, parameterize, batch, update, query |
ResultSetHandler |
handleResultSets, handleOutputParameters |
args
设置对应接口中的哪一个方法,比如 Executor
中 query
方法因为重载原因,方法有多个,args 就是指明参数类型,从而确定是哪一个方法。
这里的方法和上面的方法的要怎么理解呢?可以这样理解,上面的那个方法(method)是大类的方法,比如update方法,下面的这个方法(args)呢,是具体的方法,用来指明你到底的哪里的update方法。
注册拦截器
使用配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration> <settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <plugins> <plugin interceptor="com.example.IbatisInterceptor"/> </plugins> </configuration>
|
然后在主配置文件中,将mybatis配置文件引入:
1 2 3
| mybatis: config-location: classpath:mybatis/config/mybatis-config.xml
|
todo:
mybatis的配置项中,有一个interceptors
配置,其参数是一个列表(java.util.List)。通过查阅相关信息发现,不能在配置文件中引用自定义的拦截器。总之是暂时没找到 具体使用方法,先放着吧。
1 2 3 4 5 6 7 8 9
| mybatis:
mapper-locations: classpath:mybatis/*.xml configuration: map-underscore-to-camel-case: true interceptors: - xxx - xxxxx
|
使用配置类
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
| import com.example.IbatisInterceptor; import org.apache.ibatis.session.SqlSessionFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
import java.util.Properties;
@Configuration public class IbatisInterceptorConfig {
@Bean public String MyBatisInterceptor(SqlSessionFactory sqlSessionFactory) { IbatisInterceptor ibatisInterceptor = new IbatisInterceptor(); Properties properties = new Properties(); properties.setProperty("driver", "mysql"); properties.setProperty("username", "root"); properties.setProperty("password", "123456");
ibatisInterceptor.setProperties(properties); sqlSessionFactory.getConfiguration().addInterceptor(ibatisInterceptor); return "interceptor"; }
}
|
使用注解
直接在拦截器类上面使用@Component
注解即可(本文中就是直接使用的注解)
使用拦截器
使用mybatis拦截器,需要实现Interceptor
接口的三个方法:
intercept()
:当方法被拦截时调用,用于设置拦截后需要执行的业务逻辑,自定义拦截器时,此方法是必须实现的。
plugin()
:在mybatis加载配置时被调用。设置是否需要对对象进行拦截
- 拦截对象:调用wrap()方法,返回一个代理对象,并执行intercept()方法
- 不拦截对象:直接返回目标原本对象,不会执行intercept()方法
setProperties()
:在mybatis加载配置时被调用。获取并设置mybatis配置文件或配置类中的property属性的值,配置类见上面注册拦截器时的演示
mybatis配置文件演示:
1 2 3 4 5 6 7 8 9 10 11 12
| <environments default="mysql"> <environment id="mysql"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments>
|
原理图:
简单使用
以拦截执行器方法为例。
示例:拦截更新操作,在更新操作时统一添加 “更新时间 ”字段,同时打印当前sql语句。
从语义角度分析:
insert、delete、update都是属于更新(update)操作;
而select属于查询(query)操作。
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
| import com.example.bean.User; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.plugin.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component;
import java.lang.reflect.Field; import java.sql.Date; import java.time.LocalDateTime; import java.util.Properties;
@Component @Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})}) public class IbatisInterceptor implements Interceptor {
private Logger logger = LoggerFactory.getLogger(IbatisInterceptor.class);
@Override public Object intercept(Invocation invocation) throws Throwable {
Object[] args = invocation.getArgs(); SqlCommandType sqlCommandType = null;
for (Object object : args) {
if (object instanceof MappedStatement) { MappedStatement mappedStatement = (MappedStatement) object; sqlCommandType = mappedStatement.getSqlCommandType();
BoundSql boundSql = mappedStatement.getBoundSql(object); String sql = boundSql.getSql();
logger.info("操作类型: {},操作语句:{}", sqlCommandType, sql); } if (object instanceof User) { if (SqlCommandType.UPDATE == sqlCommandType) { Field updateTime = object.getClass().getDeclaredField("updateTime"); updateTime.setAccessible(true); updateTime.set(object, Date.valueOf(LocalDateTime.now().toLocalDate())); }
} } return invocation.proceed(); }
@Override public Object plugin(Object target) { if (target instanceof Executor) { return Plugin.wrap(target, this); } return target; }
@Override public void setProperties(Properties properties) {
} }
|
拦截多个请求
示例:只做简单示例,不涉及业务逻辑
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
| import com.example.bean.User; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.plugin.*; import org.apache.ibatis.session.RowBounds; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component;
import java.sql.PreparedStatement; import java.util.Properties;
@Component @Intercepts({@Signature(type = Executor.class,method = "query", args = {MappedStatement.class, Object.class}), @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}), @Signature(type = Executor.class, method = "createCacheKey", args = {MappedStatement.class, Object.class, RowBounds.class, BoundSql.class}), @Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),
}) public class IbatisInterceptor implements Interceptor {
private Logger logger = LoggerFactory.getLogger(IbatisInterceptor.class);
@Override public Object intercept(Invocation invocation) throws Throwable {
Object[] args = invocation.getArgs(); SqlCommandType sqlCommandType = null;
for (Object object : args) {
if (object instanceof MappedStatement) { MappedStatement mappedStatement = (MappedStatement) object; sqlCommandType = mappedStatement.getSqlCommandType();
if (object instanceof User) { if (SqlCommandType.UPDATE == sqlCommandType) {
} else if (SqlCommandType.SELECT == sqlCommandType) {
} else if (SqlCommandType.DELETE == sqlCommandType) {
} else if (SqlCommandType.INSERT == sqlCommandType) {
} } }else if(object instanceof PreparedStatement){ PreparedStatement preparedStatement = (PreparedStatement) object;
} if (object instanceof User) { if (SqlCommandType.UPDATE == sqlCommandType) {
} else if (SqlCommandType.SELECT == sqlCommandType) {
} else if (SqlCommandType.DELETE == sqlCommandType) {
} else if (SqlCommandType.INSERT == sqlCommandType) {
} } } return invocation.proceed(); } @Override public Object plugin(Object target) { if (target instanceof Executor) { return Plugin.wrap(target, this); } return target; } @Override public void setProperties(Properties properties) {
} }
|
拦截并修改sql语句
从上面的示例中我们能够发现,是可以在拦截器中获取到sql语句的,那么我们就可以通过反射修改sql语句。
将通过id删除数据的sql语句改为通过name删除的sql语句:
有两种方法:
第一种是通过Statement修改
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
| import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.*; import org.apache.ibatis.plugin.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component;
import java.util.Properties;
@Component @Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})}) public class IbatisInterceptor implements Interceptor {
private Logger logger = LoggerFactory.getLogger(IbatisInterceptor.class);
@Override public Object intercept(Invocation invocation) throws Throwable {
Object[] args = invocation.getArgs(); SqlCommandType sqlCommandType = null;
for (Object object : args) {
if (object instanceof MappedStatement) { MappedStatement mappedStatement = (MappedStatement) object; sqlCommandType = mappedStatement.getSqlCommandType();
BoundSql boundSql = mappedStatement.getBoundSql(object); String sql = boundSql.getSql();
logger.info("操作类型: {},操作语句:{}", sqlCommandType, sql);
String newSql = "delete from users where u_name=?";
BoundSql newBoundSql = new BoundSql(mappedStatement.getConfiguration(), newSql, boundSql.getParameterMappings(), boundSql.getParameterObject());
MappedStatement neMappedStatement = copyFromMappedStatement(mappedStatement, new BoundSqlSqlSource(newBoundSql)); for (ParameterMapping mapping : boundSql.getParameterMappings()) { String prop = mapping.getProperty(); if (boundSql.hasAdditionalParameter(prop)) { newBoundSql.setAdditionalParameter(prop, boundSql.getAdditionalParameter(prop)); } }
final Object[] queryArgs = invocation.getArgs(); int i = 0; queryArgs[i] = neMappedStatement; } } return invocation.proceed(); }
@Override public Object plugin(Object target) { if (target instanceof Executor) { return Plugin.wrap(target, this); } return target; }
@Override public void setProperties(Properties properties) {
}
private MappedStatement copyFromMappedStatement(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(); }
public static class BoundSqlSqlSource implements SqlSource { private BoundSql boundSql;
public BoundSqlSqlSource(BoundSql boundSql) { this.boundSql = boundSql; }
public BoundSql getBoundSql(Object parameterObject) { return boundSql; } } }
|
第二种是直接通过反射修改:
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
| import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.plugin.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component;
import java.lang.reflect.Field; import java.util.Properties;
@Component @Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})}) public class IbatisInterceptor implements Interceptor {
private Logger logger = LoggerFactory.getLogger(IbatisInterceptor.class);
@Override public Object intercept(Invocation invocation) throws Throwable {
Object[] args = invocation.getArgs(); SqlCommandType sqlCommandType = null;
for (Object object : args) {
if (object instanceof MappedStatement) { MappedStatement mappedStatement = (MappedStatement) object; sqlCommandType = mappedStatement.getSqlCommandType();
BoundSql boundSql = mappedStatement.getBoundSql(object); String sql = boundSql.getSql();
logger.info("操作类型: {},操作语句:{}", sqlCommandType, sql);
String newSql = "delete from users where u_name=?";
Field field = boundSql.getClass().getDeclaredField("sql"); field.setAccessible(true); field.set(boundSql, newSql); } } return invocation.proceed(); }
@Override public Object plugin(Object target) { if (target instanceof Executor) { return Plugin.wrap(target, this); } return target; }
@Override public void setProperties(Properties properties) {
}
}
|
最后来个图吧: