Java?mybatis?開發(fā)自定義插件
介紹
MyBatis 允許你在映射語句執(zhí)行過程中的某一點(diǎn)進(jìn)行攔截調(diào)用。比如執(zhí)行前、執(zhí)行后或者對SQL結(jié)果集處理、sql入?yún)⑻幚淼龋@樣就可以在不修改mybatis源碼的情況下對sql執(zhí)行的過程或結(jié)果進(jìn)行修改,實(shí)現(xiàn)了解耦。mybatis 是在動(dòng)態(tài)代理的基礎(chǔ)上實(shí)現(xiàn)的。
使用場景
如果業(yè)務(wù)中需要設(shè)置一些通用數(shù)據(jù)庫操作,比如創(chuàng)建時(shí)間、創(chuàng)建人等通用字段又或者是分頁操作等,這類都可以使用插件開發(fā)方式,PageHelper就是基于Interceptor的一個(gè)mybatis插件。
Interceptor攔截器
public interface Interceptor { /** * 子類攔截器必須要實(shí)現(xiàn)的方法, * 在該方法對內(nèi)自定義攔截邏輯 * @param invocation * @return * @throws Throwable */ Object intercept(Invocation invocation) throws Throwable; /** 生成目標(biāo)類的代理對象 * 也可以根據(jù)需求不返回代理對象,這種情況下這個(gè)攔截器將不起作用 * 無特殊情況使用默認(rèn)的即可 * @param target * @return */ default Object plugin(Object target) { return Plugin.wrap(target, this); } /** * 設(shè)置變量 * 在注冊攔截器的時(shí)候設(shè)置變量,在這里可以獲取到 * @param properties */ default void setProperties(Properties properties) { // NOP } }
InterceptorChain攔截器鏈
在org.apache.ibatis.plugin包下有個(gè)InterceptorChain類,該類有個(gè)interceptors屬性,所有實(shí)現(xiàn)了Interceptor接口的攔截器都會被存儲到interceptors中。
源碼如下:
public class InterceptorChain { private final List<Interceptor> interceptors = new ArrayList<>(); /** * 讓目標(biāo)類在所有的攔截器中生成代理對象,并返回代理對象 * @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); } }
攔截方法
默認(rèn)情況下,MyBatis 允許使用插件來攔截Executor 、ParameterHandler 、ResultSetHandler 、StatementHandler 接口下面的方法。如果系統(tǒng)中有配置自定義插件,默認(rèn)情況下,系統(tǒng)會把上面四個(gè)類的默認(rèn)子類都作為目標(biāo)類來讓所有的攔截器進(jìn)行攔截, 以保證所有的攔截器都能對Executor 、ParameterHandler 、ResultSetHandler 、StatementHandler子類進(jìn)行攔截。
源碼如下: 在org.apache.ibatis.session.Configuration類中
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql); // 使用攔截器進(jìn)行攔截 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); // 使用攔截器進(jìn)行攔截 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); // 使用攔截器進(jìn)行攔截 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); } // 使用攔截器進(jìn)行攔截 executor = (Executor) interceptorChain.pluginAll(executor); return executor; }
注解
Intercepts
Intercepts的作用是攔截Signature注解數(shù)組中指定的類的方法。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Intercepts { /** * Returns method signatures to intercept. * Signature注解列表 * @return method signatures */ Signature[] value(); }
Signature
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. * 要攔截的類的方法的參數(shù)列表 * @return java types for method argument */ Class<?>[] args(); }
示例
步驟
- 1、實(shí)現(xiàn)org.apache.ibatis.plugin.Interceptor接口
- 2、添加Intercepts和Signature注解
- 3、根據(jù)需求實(shí)現(xiàn)Interceptor方法邏輯
入門使用
這里會寫兩個(gè)使用示例,一個(gè)是動(dòng)態(tài)給屬性賦值,一個(gè)是打印SQL。
表結(jié)構(gòu):
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 '創(chuàng)建日期', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
實(shí)體類:
public class UserInfo { private Long id; private String gender; private String userName; private Date createDate; // 省略get、set方法 }
動(dòng)態(tài)給屬性賦值
在創(chuàng)建表時(shí),有些是每個(gè)表都有的參數(shù),比如創(chuàng)建時(shí)間、修改時(shí)間等,這類參數(shù)如果在每個(gè)類進(jìn)行保存或修改的時(shí)候都進(jìn)行設(shè)值的話就有點(diǎn)重復(fù)操作了,所以可以通過mybatis插件進(jìn)行處理。
1、Interceptor 實(shí)現(xiàn)類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(); //調(diào)用createDate 寫方法 writeMethod.invoke(parameter , new Date()); return executor.update(mappedStatement, parameter); } @Override public Object plugin(Object target) { return Plugin.wrap(target , this); } /** * 設(shè)置變量 * * @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(); } }
查看數(shù)據(jù)庫,可以看到在沒有給createDate屬性收到賦值的情況下,通過攔截器進(jìn)行賦值,最后是保存到數(shù)據(jù)庫中了。
打印SQL
Interceptor 實(shí)現(xiàn)類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(); //方法參數(shù) 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); //方法執(zhí)行 final Object returnValue = invocation.proceed(); return returnValue; } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { } /** * 獲取參數(shù) * @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; } }
使用同樣的方法進(jìn)行測試,查看控制臺打印結(jié)果:
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')
mybatis自定義插就到這里了,其實(shí)操作也簡單,用好了也很強(qiáng)大。
到此這篇關(guān)于Java mybatis 開發(fā)自定義插件的文章就介紹到這了,更多相關(guān)Java mybatis 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringCloud Finchley+Spring Boot 2.0 集成Consul的方法示例(1.2版本)
這篇文章主要介紹了SpringCloud Finchley+Spring Boot 2.0 集成Consul的方法示例(1.2版本),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-08-08javafx tableview鼠標(biāo)觸發(fā)更新屬性詳解
這篇文章主要為大家詳細(xì)介紹了javafx tableview鼠標(biāo)觸發(fā)更新屬性的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08Java如何通過線程解決生產(chǎn)者/消費(fèi)者問題
這篇文章主要介紹了Java如何通過線程解決生產(chǎn)者/消費(fèi)者問題,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2020-10-10Springmvc國際化自動(dòng)配置代碼實(shí)現(xiàn)
這篇文章主要介紹了Springmvc國際化自動(dòng)配置代碼實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04解決String字符串轉(zhuǎn)JSONObject順序不對的問題
這篇文章主要介紹了解決String字符串轉(zhuǎn)JSONObject順序不對的問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12