您好,登錄后才能下訂單哦!
這篇文章主要介紹“Java mybatis怎么開發自定義插件”,在日常操作中,相信很多人在Java mybatis怎么開發自定義插件問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Java mybatis怎么開發自定義插件”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
MyBatis 允許你在映射語句執行過程中的某一點進行攔截調用。比如執行前、執行后或者對SQL結果集處理、sql入參處理等,這樣就可以在不修改mybatis源碼的情況下對sql執行的過程或結果進行修改,實現了解耦。mybatis 是在動態代理的基礎上實現的。
如果業務中需要設置一些通用數據庫操作,比如創建時間、創建人等通用字段又或者是分頁操作等,這類都可以使用插件開發方式,PageHelper就是基于Interceptor的一個mybatis插件。
public interface Interceptor { /** * 子類攔截器必須要實現的方法, * 在該方法對內自定義攔截邏輯 * @param invocation * @return * @throws Throwable */ Object intercept(Invocation invocation) throws Throwable; /** 生成目標類的代理對象 * 也可以根據需求不返回代理對象,這種情況下這個攔截器將不起作用 * 無特殊情況使用默認的即可 * @param target * @return */ default Object plugin(Object target) { return Plugin.wrap(target, this); } /** * 設置變量 * 在注冊攔截器的時候設置變量,在這里可以獲取到 * @param properties */ default void setProperties(Properties properties) { // NOP } }
在org.apache.ibatis.plugin包下有個InterceptorChain類,該類有個interceptors屬性,所有實現了Interceptor接口的攔截器都會被存儲到interceptors中。
源碼如下:
public class InterceptorChain { private final List<Interceptor> interceptors = new ArrayList<>(); /** * 讓目標類在所有的攔截器中生成代理對象,并返回代理對象 * @param target * @return */ public Object pluginAll(Object target) { for (Interceptor interceptor : interceptors) { target = interceptor.plugin(target); } return target; } /** * 添加過濾器 * @param interceptor */ public void addInterceptor(Interceptor interceptor) { interceptors.add(interceptor); } public List<Interceptor> getInterceptors() { return Collections.unmodifiableList(interceptors); } }
默認情況下,MyBatis 允許使用插件來攔截Executor 、ParameterHandler 、ResultSetHandler 、StatementHandler 接口下面的方法。如果系統中有配置自定義插件,默認情況下,系統會把上面四個類的默認子類都作為目標類來讓所有的攔截器進行攔截, 以保證所有的攔截器都能對Executor 、ParameterHandler 、ResultSetHandler 、StatementHandler子類進行攔截。
源碼如下: 在org.apache.ibatis.session.Configuration類中
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql); // 使用攔截器進行攔截 parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler); return parameterHandler; } public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql) { ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds); // 使用攔截器進行攔截 resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler); return resultSetHandler; } public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); // 使用攔截器進行攔截 statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler; } public Executor newExecutor(Transaction transaction) { return newExecutor(transaction, defaultExecutorType); } public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } if (cacheEnabled) { executor = new CachingExecutor(executor); } // 使用攔截器進行攔截 executor = (Executor) interceptorChain.pluginAll(executor); return executor; }
Intercepts的作用是攔截Signature注解數組中指定的類的方法。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Intercepts { /** * Returns method signatures to intercept. * Signature注解列表 * @return method signatures */ Signature[] value(); }
Signature注解作用是攔截指定類的方法。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({}) public @interface Signature { /** * Returns the java type. * 要攔截的類 * @return the java type */ Class<?> type(); /** * Returns the method name. * 要攔截的類的方法 * @return the method name */ String method(); /** * Returns java types for method argument. * 要攔截的類的方法的參數列表 * @return java types for method argument */ Class<?>[] args(); }
1、實現org.apache.ibatis.plugin.Interceptor接口
2、添加Intercepts和Signature注解
3、根據需求實現Interceptor方法邏輯
這里會寫兩個使用示例,一個是動態給屬性賦值,一個是打印SQL。
表結構:
CREATE TABLE `users` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `gender` varchar(20) DEFAULT NULL, `userName` text NOT NULL, `create_date` datetime DEFAULT NULL COMMENT '創建日期', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
實體類:
public class UserInfo { private Long id; private String gender; private String userName; private Date createDate; // 省略get、set方法 }
在創建表時,有些是每個表都有的參數,比如創建時間、修改時間等,這類參數如果在每個類進行保存或修改的時候都進行設值的話就有點重復操作了,所以可以通過mybatis插件進行處理。
1、Interceptor 實現類InsertInterceptor:
@Intercepts({ @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}) }) public class InsertInterceptor implements Interceptor { private Properties properties; @Override public Object intercept(Invocation invocation) throws Throwable { final Object[] args = invocation.getArgs(); MappedStatement mappedStatement= (MappedStatement) args[0]; Object parameter = args[1]; Executor executor = (Executor) invocation.getTarget(); final Class<?> parameterClass = parameter.getClass(); final String createDate = properties.getProperty("createDate"); //獲取createDate 屬性描述器 final PropertyDescriptor propertyDescriptor = new PropertyDescriptor(createDate , parameterClass); //獲取createDate 寫方法 final Method writeMethod = propertyDescriptor.getWriteMethod(); //調用createDate 寫方法 writeMethod.invoke(parameter , new Date()); return executor.update(mappedStatement, parameter); } @Override public Object plugin(Object target) { return Plugin.wrap(target , this); } /** * 設置變量 * * @param properties */ @Override public void setProperties(Properties properties) { this.properties = properties; } }
2、mybatis配置文件中注冊InsertInterceptor
<plugins> <plugin interceptor="plugin.PrintSqlPlugin"/> <plugin interceptor="plugin.InsertInterceptor"> <property name="createDate" value="createDate"/> </plugin> </plugins>
3、測試
public class UserTest { private final static SqlSessionFactory sqlSessionFactory; static { String resource = "mybatis-config.xml"; Reader reader = null; try { reader = Resources.getResourceAsReader(resource); } catch (IOException e) { System.out.println(e.getMessage()); } sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); } @Test public void insert(){ SqlSession sqlSession = sqlSessionFactory.openSession(); UserInfo userInfo = new UserInfo(); userInfo.setUserName("test1"); userInfo.setGender("male"); UserInfoMapper mapper = sqlSession.getMapper(UserInfoMapper.class); mapper.insertUser(userInfo); sqlSession.commit(); sqlSession.close(); } }
查看數據庫,可以看到在沒有給createDate屬性收到賦值的情況下,通過攔截器進行賦值,最后是保存到數據庫中了。
Interceptor 實現類PrintSqlPlugin:
@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}), @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}) }) public class PrintSqlPlugin implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { //被代理對象 Object target = invocation.getTarget(); //代理方法 Method method = invocation.getMethod(); //方法參數 Object[] args = invocation.getArgs(); MappedStatement mappedStatement= (MappedStatement) args[0]; Object parameter = args[1]; final BoundSql mappedStatementBoundSql = mappedStatement.getBoundSql(parameter); System.err.println("BoundSql="+mappedStatementBoundSql.getSql()); final Configuration configuration = mappedStatement.getConfiguration(); final String showSql = showSql(configuration, mappedStatementBoundSql); System.err.println("sql="+showSql); //方法執行 final Object returnValue = invocation.proceed(); return returnValue; } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { } /** * 獲取參數 * @param obj * @return */ private static String getParameterValue(Object obj) { String value = null; if (obj instanceof String) { value = "'" + obj.toString() + "'"; value = value.replaceAll("\\\\", "\\\\\\\\"); value = value.replaceAll("\\$", "\\\\\\$"); } else if (obj instanceof Date) { DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA); value = "'" + formatter.format(obj) + "'"; } else { if (obj != null) { value = obj.toString(); } else { value = ""; } } return value; } /** * 打印SQL * @param configuration * @param boundSql * @return */ public static String showSql(Configuration configuration, BoundSql boundSql) { Object parameterObject = boundSql.getParameterObject(); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); String sql = boundSql.getSql().replaceAll("[\\s]+", " "); if (parameterMappings.size() > 0 && parameterObject != null) { TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { sql = sql.replaceFirst("\\?", getParameterValue(parameterObject)); } else { MetaObject metaObject = configuration.newMetaObject(parameterObject); for (ParameterMapping parameterMapping : parameterMappings) { String propertyName = parameterMapping.getProperty(); if (metaObject.hasGetter(propertyName)) { Object obj = metaObject.getValue(propertyName); sql = sql.replaceFirst("\\?", getParameterValue(obj)); } else if (boundSql.hasAdditionalParameter(propertyName)) { Object obj = boundSql.getAdditionalParameter(propertyName); sql = sql.replaceFirst("\\?", getParameterValue(obj)); } } } } return sql; } }
使用同樣的方法進行測試,查看控制臺打印結果:
BoundSql=insert into users (gender, userName ,create_date) values(? , ?, ?)
sql=insert into users (gender, userName ,create_date) values('male' , 'test2', '2022-1-14 18:40:08')
到此,關于“Java mybatis怎么開發自定義插件”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。