MyBatis 允许在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
除了用插件来修改 MyBatis 核心行为之外,还可以通过完全覆盖配置类来达到目的。只需继承后覆盖其中的每个方法,再把它传递到 SqlSessionFactoryBuilder.build(myConfig) 方法即可。
一、可拦截的接口
1.Executor-执行器
public interface Executor { // 不需要 ResultHandler ResultHandler NO_RESULT_HANDLER = null; // 更新 int update(MappedStatement ms, Object parameter) throws SQLException; // 查询(先查缓存),带分页,BoundSql <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException; // 查询,带分页 <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException; // 查询游标 <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException; // 刷新(Statement)批处理语句 List<BatchResult> flushStatements() throws SQLException; // 提交事务,参数为是否要强制 void commit(boolean required) throws SQLException; // 回滚事务,参数为是否要强制 void rollback(boolean required) throws SQLException; // 创建 CacheKey CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql); // 判断是否缓存了,通过 CacheKey boolean isCached(MappedStatement ms, CacheKey key); // 清理 Session(本地一级) 缓存 void clearLocalCache(); // 延迟加载 void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType); // 获取事务 Transaction getTransaction(); // 关闭连接 void close(boolean forceRollback); // 是否关闭 boolean isClosed(); // 设置 Executor void setExecutorWrapper(Executor executor); }
2.ParameterHandler-参数处理器
public interface ParameterHandler { // 获取参数 Object getParameterObject(); // 设置参数 void setParameters(PreparedStatement ps) throws SQLException; }
3.ResultSetHandler-结果集处理器
public interface ResultSetHandler { // 将结果集转化成 List <E> List<E> handleResultSets(Statement stmt) throws SQLException; // 将结果集转化成 Cursor <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException; // 处理存储过程的 OUT(输出) 参数 void handleOutputParameters(CallableStatement cs) throws SQLException; }
4.StatementHandler-SQL语句处理器
public interface StatementHandler { // 准备语句 Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException; // 参数处理 void parameterize(Statement statement) throws SQLException; // 批量处理 void batch(Statement statement) throws SQLException; // 更新处理 int update(Statement statement) throws SQLException; // 查询处理,结果给 ResultHandler <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException; <E> Cursor<E> queryCursor(Statement statement) throws SQLException; // 获取绑定 SQL 语句 BoundSql getBoundSql(); // 获取参数处理器 ParameterHandler getParameterHandler(); }
二、自定义插件
实现 Interceptor 接口,并指定想要拦截的方法签名。最后在 xml 文件中配置全类名即可。
1.bean
@Alias("myUser") public class MyUser implements Serializable { private Integer id; private String name; private Integer age;
2.dao接口
public interface MyUserMapperAnnotation { @Select("select * from myuser") List<MyUser> selectMyUser(String a); }
3.mybatis-config.xml
<?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> <!-- 注意 plugins 在配置文件中的位置 properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, plugins?, environments?, databaseIdProvider?, mappers? --> <plugins> <plugin interceptor="com.plugins.ExamplePlugin"> <property name="someProperty" value="100"/> </plugin> </plugins> <environments default="development-mysql"> <environment id="development-mysql"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://192.168.8.136:3306/mybatis?allowMultiQueries=true"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <mappers> <mapper class="com.dao.MyUserMapperAnnotation"/> </mappers> </configuration>
4.ExamplePlugin
/** * @Intercepts 拦截器注解,包括一个或多个 @Signature * @Signature 拦截的目标类信息,包括 type、method、args * * type 要拦截的接口类型 * * method 接口中的方法名 * * args 方法的所有入参类型 */ @Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}) }) public class ExamplePlugin implements Interceptor { /** * 拦截目标对象的目标方法的执行,将自定义逻辑写在该方法中 */ @Override public Object intercept(Invocation invocation) throws Throwable { System.out.println("ExamplePlugin...intercept:" + invocation.getMethod()); // MetaObject 是 Mybatis 提供的一个用于访问对象属性的对象 MetaObject metaObject = SystemMetaObject.forObject(invocation); System.out.println("当前拦截到的对象:" + metaObject.getValue("target")); System.out.println("SQL语句:" + metaObject.getValue("target.delegate.boundSql.sql")); System.out.println("SQL语句入参:" + metaObject.getValue("target.delegate.parameterHandler.parameterObject")); System.out.println("SQL语句类型:" + metaObject.getValue("target.delegate.parameterHandler.mappedStatement.sqlCommandType")); System.out.println("Mapper方法全路径名:" + metaObject.getValue("target.delegate.parameterHandler.mappedStatement.id")); // 修改 SQL 语句 String newSQL = metaObject.getValue("target.delegate.boundSql.sql") + " limit 2"; metaObject.setValue("target.delegate.boundSql.sql", newSQL); System.out.println("修改后SQL语句:" + newSQL); // 返回执行结果 return invocation.proceed(); } /** * 为目标对象创建一个代理对象,使用 Plugin.wrap(target,this) 即可 * @param target 上次包装的代理对象 * @return 本次包装过的代理对象 */ @Override public Object plugin(Object target) { System.out.println("ExamplePlugin...plugin:" + target); return Plugin.wrap(target, this); } /** * 获取自定义配置参数 * @param properties */ @Override public void setProperties(Properties properties) { System.out.println("插件配置信息:" + properties); System.out.println("someProperty:" + properties.get("someProperty")); } }
5.Main
public static void main(String[] args) throws IOException { SqlSession session = null; try { InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); session = sqlSessionFactory.openSession(); MyUserMapperAnnotation mapper = session.getMapper(MyUserMapperAnnotation.class); System.out.println(mapper.selectMyUser("asdsad")); } finally { if (session != null) { session.close(); } } }
http://www.mybatis.org/mybatis-3/zh/configuration.html#plugins