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