<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
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(); }
這裡會寫兩個使用範例,一個是動態給屬性賦值,一個是列印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')
mybatis自定義插就到這裡了,其實操作也簡單,用好了也很強大。
到此這篇關於Java mybatis 開發自定義外掛的文章就介紹到這了,更多相關Java mybatis 內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45