在前面的文章中,筆者詳細介紹了 🔗MyBatis 框架的底層框架與運行流程,而且在理解運行流程的基礎上手寫了一個本身的 MyBatis 框架。看完前兩篇文章後,相信讀者對 MyBatis 的偏底層原理和執行流程已經有了本身的認知,而且對其在實際開發過程當中使用步驟也已經是輕車熟路。所謂實踐是檢驗真理的惟一標準,本文將爲你們介紹一些 MyBatis 使用中的一些實用插件與自定義插件。本文涉及到的代碼已上傳至 GitHub: 🔗mypagehelper-demo 。html
話很少說,如今開始🔛🔛🔛!前端
在編寫 Java 程序時常常會用到不少實體類對象 ,其建立的通常流程就是定義成員變量,而後定義對應的 Constructor(有參/無參構造方法)、Getter and Setter 方法、toString() 方法等等。在 IDEA 中,能夠經過 alt + insert
快捷鍵來快速插入這些方法,操做起來感受仍是很方便的。可是,在實際的業務開發中這些實體對象的屬性可能常常發生變化(成員變量命名變化、成員變量個數變化等等),好比在 Web 項目的開發中入參和出參的 DTO 類的屬性常常會有所變化。這樣在每次發生屬性變化時,都須要去修改爲員變量對應的構造方法、 Getter and Setter 方法以及 toString() 方法等等,這些操做既繁瑣又浪費時間尚未技術含量,下降了實際的開發效率。對於這些問題,Lombok 給出了完美的解決方案。java
Lombok 是一種 Java 實用工具,它經過註解方式來幫助開發人員消除代碼的冗長。在 IDEA 的插件庫搜索 Lombok 即可完成插件的安裝,同時在項目裏引入 Lombok 依賴能夠提供編譯支持。git
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.8</version> <scope>provided</scope> </dependency>
在 Lombok 的包中會提供不少註解,有的做用在類上,有的做用在變量上,而有的註解在方法上,下面會對實際開發中經常使用的註解進行介紹。github
註解名稱 | 做用位置 | 做用 | 註解效果 |
---|---|---|---|
@Data | 類 | 爲類提供 Getter and Setter、equals、canEqual、hashCode、toString 方法 | @Date 效果 |
@Value | 類 | 爲類提供全參構造、equals、hashCode、toString 方法,爲類的屬性提供 Getter 方法 | @Value 效果 |
@AllArgsConstructor | 類 | 爲類提供一個全參構造 | @AllArgsConstructor 效果 |
@NoArgsConstructor | 類 | 爲類提供一個無參構造 | @NoArgsConstructor 效果 |
@EqualsAndHashCode | 類 | 爲類提供 equals、canEqual 以及hashcode 方法 | @EqualsAndHashCode 效果 |
@toString | 類 | 爲類提供 toString 方法 | @toString 效果 |
@Getter | 類或者屬性 | 爲類的全部屬性或單個屬性提供 Getter 方法 | @Getter 效果 |
@Setter | 類或者屬性 | 爲類的全部屬性或單個屬性提供 Setter 方法 | @Setter 效果 |
@NonNull | 屬性 | 爲屬性提供非空檢查,若是爲空則拋出空指針異常 | @NonNull 效果 |
@RequiredArgsConstructor | 類 | 使用 帶@NonNull 或 final 修飾的屬性來構造類的構造方法 | @RequiredArgsConstructor 效果 |
在 MyBatis 的使用中靈活搭配 Lombok 的各類註解可以很大程度上簡化代碼,提升開發效率,關於更多 Lombok 插件的使用可參見其官網:https://projectlombok.org/ 。sql
實際開發中遇到查詢數據庫表給前端返回信息時,常須要對查詢結果進行分頁,使用 PageHelper 插件可以方便快捷地實現分頁要求。 PageHelper 是開源的分頁插件,支持任何單表或多表的分頁。在實際開發中,推薦使用 maven 添加依賴的方式引入插件:數據庫
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.2.0</version> </dependency>
在 MyBatis 框架的配置文件中提供了 plugins 標籤用於配置 MyBatis 插件,所以在使用 PageHelper 時須要把插件的相關配置寫到 MyBatis 的配置文件 mybatis-config.xml 中,以下所示:後端
<plugins> <!-- com.github.pagehelper爲PageHelper類所在包名 --> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <property name="param1" value="value1"/> </plugin> </plugins>
這裏須要注意的是:在 MyBatis 配置文件中,各個標籤的順序有嚴格的要求,務必在正確的位置添加 PageHelper 的配置。相應的標籤順序見下方:緩存
properties, settings, typeAliases, typeHandlers, objectFactory, objectWrapperFactory, plugins, environments, databaseIdProvided, mappersmybatis
在添加了 PageHelper 的配置後,就能夠在實際開發中利用該插件來實現分頁。仍是採用以前的學生表案例來編寫相應的測試方法,以下所示:
public class StudentTest { private InputStream in; private SqlSession sqlSession; @Before public void init() throws IOException { // 讀取MyBatis的配置文件 in = Resources.getResourceAsStream("mybatis-config.xml"); // 建立SqlSessionFactory的構建者對象 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); // 使用builder建立SqlSessionFactory對象 SqlSessionFactory factory = builder.build(in); // 使用factory建立sqlSession對象並設置自動提交事務 sqlSession = factory.openSession(true); } @Test public void getAllStudents() { // 定義分頁相關參數 int pageNum = 1, pageSize = 3; StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class); // 使用分頁插件 PageHelper.startPage(pageNum, pageSize); List<Student> students = studentMapper.findAll(); // 輸出結果 System.out.println(students); } @After public void close() throws IOException { // 關閉資源 sqlSession.close(); in.close(); } }
能夠採用 PageHelper.startPage(int pageNum, int pageSize)
來快速實現分頁操做,其中 int pageNum
用於指定當前的頁碼,而 int pageSize
用於指定每一頁展現的記錄數。這裏須要注意的是:只有緊跟在 PageHelper.startPage 方法後的第一個 Mybatis 的查詢(Select)方法會被分頁。所以在調用 PageHelper.startPage 方法後須要緊跟 Mapper 接口中定義的查詢方法,不然分頁插件將失效。對於上面的測試方法,在運行後獲得以下結果:
經過打印的日誌發現本來 SELECT * FROM student
的 SQL 語句在配置了 PageHelper 插件後會在語句末尾加入 LIMIT
的分頁操做,同時傳入指定的 pageSize
參數。在最後的查詢結果輸出中也能夠看出實際的總記錄數爲 total = 4
,而查詢結果只顯示了第一頁的三條記錄,成功實現了對查詢結果分頁的操做。
上面介紹的 PageHelper.startPage() 方法最大的侷限在於只能對緊跟在其後的 MyBatis 的查詢操做的結果進行分頁。然而,在實際的後端開發中常常須要對多表進行查詢並對結果進行聚合,而後給前端傳一個結果集合,這種時候如何實現分頁操做呢?在這種狀況下,仍然能夠利用 PageHelper 插件進行手工分頁,定義用於分頁請求的 pageRequest()
方法以及相應的測試類,以下所示:
/** * 分頁請求 * @param pageNum 指定頁碼 * @param pageSize 指定每頁的記錄數 * @param list 待分頁的集合 * @param <T> 集合類型 * @return 分頁後的集合 */ private <T> List<T> pageRequest(int pageNum, int pageSize, List<T> list){ // 根據pageNum和pageSize構建Page類 Page<T> page = new Page<T>(pageNum, pageSize); // 設置page對象的總記錄數屬性 page.setTotal(list.size()); // 計算分頁的開始和結束索引 int startIndex = (pageNum - 1) * pageSize; int endIndex = Math.min(startIndex + pageSize, list.size()); // 從待分頁的集合獲取須要展現的內容添加到page對象 page.addAll(list.subList(startIndex, endIndex)); // 返回分頁後的集合 return page.getResult(); } @Test public void testPage() { // 定義分頁相關參數 int pageNum = 2, pageSize = 3; // 準備List<Student>集合 List<Student> students = new ArrayList<Student>(); students.add(new Student(1, "張A","男")); students.add(new Student(2, "張B","男")); students.add(new Student(3, "張C","男")); students.add(new Student(4, "張D","男")); students.add(new Student(5, "張E","男")); // 分頁 List<Student> results = pageRequest(pageNum, pageSize, students); System.out.println(results); }
經過構建上面的 pageRequest 方法,咱們實現了一個簡單的手工分頁,經過調用該方法就可以實現對已有集合的分頁,經過運行測試方法能夠獲得以下結果。
從結果易知在面對已存在的 List 集合時,咱們基於 PageHelper 插件構建的 pageRequest 方法仍起到了分頁的做用。這裏指定的頁碼 pageNum = 2,pageSize = 3
,待分頁的集合總記錄爲五條,結果顯示了集合中的最後兩條記錄,分頁結果正確。
在本小節中介紹了 PageHelper.startPage 方法以及手工定義 pageRequest 方法的兩種基於 PageHelper 插件的分頁方式,可以根據不一樣的狀況實現分頁需求。
在第一篇文章中已經對 MyBatis 框架的運行流程進行了講解,相信讀者都已知曉 MyBatis 是利用 XMLConfigBuilder 來對配置文件進行解析的。而在前文提到插件的配置是寫在 mybatis-config.xml 配置文件中,所以去 XMLConfigBuilder 類中找解析 plugins 標籤的方法。
果不其然,能夠找到解析 MyBatis 插件的 XMLConfigBuilder#pluginElement() 方法,以下所示:
private void pluginElement(XNode parent) throws Exception { if (parent != null) { // 遍歷子節點 for (XNode child : parent.getChildren()) { // 獲取interceptor標籤下攔截器的全限定類名 String interceptor = child.getStringAttribute("interceptor"); // 獲取子節點對應的屬性 Properties properties = child.getChildrenAsProperties(); // 經過上面獲取得攔截器的全限定類名構建一個攔截器實例 Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance(); // 將子節點對應的屬性設置到攔截器 interceptorInstance.setProperties(properties); // 將該攔截器實例設置到Configuration對象 configuration.addInterceptor(interceptorInstance); } } }
找到上面方法中涉及到的 Interceptor 類對於的源碼以下所示:
public interface Interceptor { Object intercept(Invocation invocation) throws Throwable; Object plugin(Object target); void setProperties(Properties properties); }
能夠看出這是一個接口,其中定義了 intercept()、plugin() 以及 setProperties() 三個方法:
對於一個插件來講,必需要先實現 Intercept 接口中的三個方法才能在 MyBatis 框架中進行配置並使用。
本節中會從 PageHelper 的源代碼來分析 MyBatis 插件的實現流程,找到關鍵類 PageInterceptor 的源碼以下:
// 壓制警告註解 @SuppressWarnings({"rawtypes", "unchecked"}) // 攔截器的註解 @Intercepts( { // 註冊攔截器簽名:指定須要被攔截的類型(type)、方法(method)和參數(args)須要被攔截 // 只包含4個參數 @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}), // 只包含6個參數 @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}), } ) public class PageInterceptor implements Interceptor { // 緩存count查詢的ms protected Cache<String, MappedStatement> msCountMap = null; private Dialect dialect; private String default_dialect_class = "com.github.pagehelper.PageHelper"; private Field additionalParametersField; private String countSuffix = "_COUNT"; @Override public Object intercept(Invocation invocation) throws Throwable { try { // 解析攔截到的參數 Object[] args = invocation.getArgs(); MappedStatement ms = (MappedStatement) args[0]; Object parameter = args[1]; RowBounds rowBounds = (RowBounds) args[2]; ResultHandler resultHandler = (ResultHandler) args[3]; Executor executor = (Executor) invocation.getTarget(); CacheKey cacheKey; BoundSql boundSql; // 因爲邏輯關係,只會進入一次 if(args.length == 4){ // 4個參數時 boundSql = ms.getBoundSql(parameter); cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql); } else { // 6個參數時 cacheKey = (CacheKey) args[4]; boundSql = (BoundSql) args[5]; } List resultList; // 調用方法判斷是否須要進行分頁,若是不須要,直接返回結果 if (!dialect.skip(ms, parameter, rowBounds)) { // 反射獲取動態參數 String msId = ms.getId(); Configuration configuration = ms.getConfiguration(); Map<String, Object> additionalParameters = (Map<String, Object>) additionalParametersField.get(boundSql); // 判斷是否須要進行count查詢 if (dialect.beforeCount(ms, parameter, rowBounds)) { String countMsId = msId + countSuffix; Long count; // 先判斷是否存在手寫的count查詢 MappedStatement countMs = getExistedMappedStatement(configuration, countMsId); if(countMs != null){ count = executeManualCount(executor, countMs, parameter, boundSql, resultHandler); } else { countMs = msCountMap.get(countMsId); // 自動建立 if (countMs == null) { // 根據當前的ms建立一個返回值爲Long類型的ms countMs = MSUtils.newCountMappedStatement(ms, countMsId); msCountMap.put(countMsId, countMs); } count = executeAutoCount(executor, countMs, parameter, boundSql, rowBounds, resultHandler); } // 處理查詢總數 // 返回true時繼續分頁查詢,false時直接返回 if (!dialect.afterCount(count, parameter, rowBounds)) { // 當查詢總數爲0時,直接返回空的結果 return dialect.afterPage(new ArrayList(), parameter, rowBounds); } } // 判斷是否須要進行分頁查詢 if (dialect.beforePage(ms, parameter, rowBounds)) { // 生成分頁的緩存key CacheKey pageKey = cacheKey; // 處理參數對象 parameter = dialect.processParameterObject(ms, parameter, boundSql, pageKey); // 調用方言獲取分頁sql String pageSql = dialect.getPageSql(ms, boundSql, parameter, rowBounds, pageKey); BoundSql pageBoundSql = new BoundSql(configuration, pageSql, boundSql.getParameterMappings(), parameter); // 設置動態參數 for (String key : additionalParameters.keySet()) { pageBoundSql.setAdditionalParameter(key, additionalParameters.get(key)); } // 執行分頁查詢 resultList = executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, pageKey, pageBoundSql); } else { // 不執行分頁的狀況下,也不執行內存分頁 resultList = executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, cacheKey, boundSql); } } else { // rowBounds用參數值,不使用分頁插件處理時,仍然支持默認的內存分頁 resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql); } return dialect.afterPage(resultList, parameter, rowBounds); } finally { dialect.afterAll(); } } // 省略…… @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { // 緩存count ms msCountMap = CacheFactory.createCache(properties.getProperty("msCountCache"), "ms", properties); String dialectClass = properties.getProperty("dialect"); if (StringUtil.isEmpty(dialectClass)) { dialectClass = default_dialect_class; } try { Class<?> aClass = Class.forName(dialectClass); dialect = (Dialect) aClass.newInstance(); } catch (Exception e) { throw new PageException(e); } dialect.setProperties(properties); String countSuffix = properties.getProperty("countSuffix"); if (StringUtil.isNotEmpty(countSuffix)) { this.countSuffix = countSuffix; } try { // 反射獲取BoundSql中的additionalParameters屬性 additionalParametersField = BoundSql.class.getDeclaredField("additionalParameters"); additionalParametersField.setAccessible(true); } catch (NoSuchFieldException e) { throw new PageException(e); } } }
簡單看完 PageHelper 插件的實現代碼以後,總結一下 MyBatis 框架中自定義插件的步驟:
本節中會借鑑 PageHelper 插件的實現思路來實現自定義一個 MyPageHelper 插件,以更好地理解 MyBatis 框架運行插件的流程。
本小節中實現了自定義的 MyPage 類,這是一個分頁返回對象,封裝了分頁的相關信息以及分頁的列表數據,以下所示:
@Getter public class MyPage<E> extends ArrayList<E> { private static final long serialVersionUID = 2630741492557235098L; /** 指定頁碼,從1開始 **/ @Setter private Integer pageNum; /** 指定每頁記錄數 **/ @Setter private Integer pageSize; /** 起始行 **/ @Setter private Integer startIndex; /** 末行 **/ @Setter private Integer endIndex; /** 總記錄數 **/ private Long total; /** 總頁數 **/ @Setter private Integer pages; // 根據pageNum、pageSize以及total設置其它屬性 public void setTotal(Long total) { this.total = total; this.pages = (int)(total / pageSize + (total % pageSize == 0 ? 0 : 1)); if (pageNum > pages) { pageNum = pages; } this.startIndex = this.pageNum > 0 ? (this.pageNum - 1) * this.pageSize : 0; this.endIndex = this.startIndex + this.pageSize * (this.pageNum > 0 ? 1 : 0); } // 獲取分頁後的結果 public List<E> getResults() { return this; } }
進一步定義一個 MyPageHelper 類來輔助分頁,該類的核心是利用 ThreadLocal 線程遍歷存儲分頁信息,代碼以下所示:
/** * 分頁幫助類 * @author chenliang258 * @date 2021/3/17 17:23 */ @SuppressWarnings("rawtypes") public class MyPageHelper { private static final ThreadLocal<MyPage> MY_PAGE_THREAD_LOCAL = new ThreadLocal<>(); public static void setMyPageThreadLocal(MyPage myPage) { MY_PAGE_THREAD_LOCAL.set(myPage); } public static MyPage geyMyPageThreadLocal() { return MY_PAGE_THREAD_LOCAL.get(); } public static void clearMyPageThreadLocal() { MY_PAGE_THREAD_LOCAL.remove(); } public static void startPage(Integer pageNum, Integer pageSize) { MyPage myPage = new MyPage(); myPage.setPageNum(pageNum); myPage.setPageSize(pageSize); setMyPageThreadLocal(myPage); } }
接下來就須要編寫 Interceptor 接口的實現類來實現相應方法,這裏定義了 MyPageInterceptor 類,代碼以下:
/** * 分頁攔截器實現 * @author chenliang258 * @date 2021/3/17 17:30 */ @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}) }) public class MyPageInterceptor implements Interceptor { private Field field; @SuppressWarnings({"rawtypes", "unchecked"}) @Override public Object intercept(Invocation invocation) throws Throwable { Executor executor = (Executor)invocation.getTarget(); Object[] args = invocation.getArgs(); MappedStatement ms = (MappedStatement)args[0]; Object parameter = args[1]; RowBounds rowBounds = (RowBounds)args[2]; ResultHandler resultHandler = (ResultHandler)args[3]; CacheKey cacheKey; BoundSql boundSql; // 4個參數 if (args.length == 4) { boundSql = ms.getBoundSql(parameter); cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql); } // 6個參數 else { cacheKey = (CacheKey)args[4]; boundSql = (BoundSql)args[5]; } // 判斷是否須要分頁 MyPage myPage = MyPageHelper.geyMyPageThreadLocal(); // 不執行分頁 if (myPage.getPageNum() <= 0) { return executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql); } // count查詢 MappedStatement countMs = newCountMappedStatement(ms); String sql = boundSql.getSql(); String countSql = "select count(1) from (" + sql + ") _count"; BoundSql countBoundSql = new BoundSql(ms.getConfiguration(), countSql, boundSql.getParameterMappings(), parameter); Map<String, Object> additionalParameters = (Map<String, Object>) field.get(boundSql); for (Map.Entry<String, Object> additionalParameter : additionalParameters.entrySet()) { countBoundSql.setAdditionalParameter(additionalParameter.getKey(), additionalParameter.getValue()); } CacheKey countCacheKey = executor.createCacheKey(countMs, parameter, rowBounds, countBoundSql); Object countResult = executor.query(countMs, parameter, RowBounds.DEFAULT, resultHandler, countCacheKey, countBoundSql); Long count = (Long)((List)countResult).get(0); myPage.setTotal(count); // 分頁查詢 String pageSql = sql + " limit " + myPage.getStartIndex() + "," + myPage.getPageSize(); BoundSql pageBoundSql = new BoundSql(ms.getConfiguration(), pageSql, boundSql.getParameterMappings(), parameter); for (Map.Entry<String, Object> additionalParameter : additionalParameters.entrySet()) { pageBoundSql.setAdditionalParameter(additionalParameter.getKey(), additionalParameter.getValue()); } CacheKey pageCacheKey = executor.createCacheKey(ms, parameter, rowBounds, pageBoundSql); List listResult = executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, pageCacheKey, pageBoundSql); myPage.addAll(listResult); // 清空線程局部變量分頁信息 MyPageHelper.clearMyPageThreadLocal(); return myPage; } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { try { field = BoundSql.class.getDeclaredField("additionalParameters"); field.setAccessible(true); } catch (NoSuchFieldException | SecurityException e) { e.printStackTrace(); } } /** * 建立count的MappedStatement * * @param ms 原始MappedStatement * @return 新的帶有分頁信息的MappedStatement */ private MappedStatement newCountMappedStatement(MappedStatement ms) { MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId() + "_count", ms.getSqlSource(), ms.getSqlCommandType()); builder.resource(ms.getResource()); builder.fetchSize(ms.getFetchSize()); builder.statementType(ms.getStatementType()); builder.keyGenerator(ms.getKeyGenerator()); if (ms.getKeyProperties() != null && ms.getKeyProperties().length != 0) { StringBuilder keyProperties = new StringBuilder(); for (String keyProperty : ms.getKeyProperties()) { keyProperties.append(keyProperty).append(","); } keyProperties.delete(keyProperties.length() - 1, keyProperties.length()); builder.keyProperty(keyProperties.toString()); } builder.timeout(ms.getTimeout()); builder.parameterMap(ms.getParameterMap()); // count查詢返回值int List<ResultMap> resultMaps = new ArrayList<>(); ResultMap resultMap = new ResultMap.Builder(ms.getConfiguration(), ms.getId() + "_count", Long.class, new ArrayList<>(0)).build(); resultMaps.add(resultMap); builder.resultMaps(resultMaps); builder.resultSetType(ms.getResultSetType()); builder.cache(ms.getCache()); builder.flushCacheRequired(ms.isFlushCacheRequired()); builder.useCache(ms.isUseCache()); return builder.build(); } }
要測試自定義的 MyPageHelper 插件,首先必需要在 mybatis-config.xml 配置文件中添加自定義插件的配置信息,以下所示:
<plugins> <!-- 使用自定義插件MyPageHelper --> <plugin interceptor="com.chiaki.mypagehelper.MyPageInterceptor" /> </plugins>
而後編寫 MyPageHelper 插件的測試方法,以下所示:
@Test public void testMyPageHelper() { Integer pageNum = 2, pageSize = 3; StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class); MyPageHelper.startPage(pageNum, pageSize); List<Student> students = studentMapper.findAll(); System.out.println(students); }
運行測試方法後,其結果以下圖所示。能夠看出在數據庫表中共有 4 條記錄,在設置了 pageNum = 2, pageSize = 3
參數後,查詢結果顯示的是第二頁的數據,僅此 1 條,結果符合預期,這也驗證了本節中自定義 MyPageHelper 分頁插件的正確性。
本文首先介紹了 MyBatis 框架使用中比較實用的 Lombok 以及 PageHelper 插件,而後從 PageHelper 插件出發簡單介紹了 MyBatis 框架中插件的解析與運行流程,並在此基礎上實現了一個自定義的 MyPageHelper 分頁插件。筆者認爲在實際應用中沒必要重複造輪子,有好用的插件直接使用就行,大大提升開發效率。可是從另一個角度看,所謂知其然也要知其因此然,從源碼去理解實現原理並可以本身動手實現一遍對於我的的進步是很是有用的。本文中只是很簡略地介紹了下 MyBatis 的插件解析與運行過程,實現的 MyPageHelper 插件也處於模仿的層面。讀者感興趣的話能夠自行去探究源碼,可以在模仿中創新是最好不過了!
Lombok 官方社區:https://projectlombok.org/
PageHelper 官方社區:https://pagehelper.github.io/
MyBatis 官網:https://mybatis.org/mybatis-3/
MyBatis 源碼倉庫:https://github.com/mybatis/mybatis-3
淺析MyBatis(一):由一個快速案例剖析MyBatis的總體架構與運行流程
淺析MyBatis(二):手寫一個本身的MyBatis簡單框架
🔚🔚🔚
以爲有用的話就點個推薦吧~