日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

带码农《手写Mybatis》进度3:实现映射器的注册和使用

發(fā)布時間:2023/12/9 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 带码农《手写Mybatis》进度3:实现映射器的注册和使用 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Python微信訂餐小程序課程視頻

https://edu.csdn.net/course/detail/36074

Python實戰(zhàn)量化交易理財系統(tǒng)

https://edu.csdn.net/course/detail/35475
作者:小傅哥
博客:https://bugstack.cn

沉淀、分享、成長,讓自己和他人都能有所收獲!😄

一、前言

如何面對復雜系統(tǒng)的設計?

我們可以把 Spring、Mybatis、Dubbo 這樣的大型框架或者一些公司內(nèi)部的較核心項目,都可以稱為復雜的系統(tǒng)。這樣的工程也不在是初學編程手里的玩具項目,沒有所謂的CRUD,更多時候要面對的都是對系統(tǒng)分層的結構設計和聚合邏輯功能的實現(xiàn),再通過層層轉換進行實現(xiàn)和調(diào)用。

這對于很多剛上道的小碼農(nóng)來說,會感覺非常難受,不知道要從哪下手,但又想著可以一口吃個胖子。其實這是不現(xiàn)實的,因為這些復雜系統(tǒng)中的框架中有太多的內(nèi)容你還沒用了解和熟悉,越是硬搞越難受,信心越受打擊。

其實對于解決這類復雜的項目問題,核心在于要將主干問題點縮小,具體的手段包括:分治、抽象和知識。運用設計模式和設計原則等相關知識,把問題空間合理切割為若干子問題,問題越小也就越容易理解和處理。就像你可以把很多內(nèi)容做成單個獨立的案例一樣,最終在進行聚合使用。

二、目標

在上一章節(jié)我們初步的了解了怎么給一個接口類生成對應的映射器代理,并在代理中完成一些用戶對接口方法的調(diào)用處理。雖然我們已經(jīng)看到了一個核心邏輯的處理方式,但在使用上還是有些刀耕火種的,包括:需要編碼告知 MapperProxyFactory 要對哪個接口進行代理,以及自己編寫一個假的 SqlSession 處理實際調(diào)用接口時的返回結果。

那么結合這兩塊問題點,我們本章節(jié)要對映射器的注冊提供注冊機處理,滿足用戶可以在使用的時候提供一個包的路徑即可完成掃描和注冊。與此同時需要對 SqlSession 進行規(guī)范化處理,讓它可以把我們的映射器代理和方法調(diào)用進行包裝,建立一個生命周期模型結構,便于后續(xù)的內(nèi)容的添加。

三、設計

鑒于我們希望把整個工程包下關于數(shù)據(jù)庫操作的 DAO 接口與 Mapper 映射器關聯(lián)起來,那么就需要包裝一個可以掃描包路徑的完成映射的注冊器類。

當然我們還要把上一章節(jié)中簡化的 SqlSession 進行完善,由 SqlSession 定義數(shù)據(jù)庫處理接口和獲取 Mapper 對象的操作,并把它交給映射器代理類進行使用。這一部分是對上一章節(jié)內(nèi)容的完善

有了 SqlSession 以后,你可以把它理解成一種功能服務,有了功能服務以后還需要給這個功能服務提供一個工廠,來對外統(tǒng)一提供這類服務。比如我們在 Mybatis 中非常常見的操作,開啟一個 SqlSession。整個設計可以如圖 3-1

  • 以包裝接口提供映射器代理類為目標,補全映射器注冊機 MapperRegistry,自動掃描包下接口并把每個接口類映射的代理類全部存入映射器代理的 HashMap 緩存中。
  • 而 SqlSession、SqlSessionFactory 是在此注冊映射器代理的上次層使用標準定義和對外服務提供的封裝,便于用戶使用。我們把使用方當成用戶 經(jīng)過這樣的封裝就就可以更加方便我們后續(xù)在框架上功能的繼續(xù)擴展了,也希望大家可以在學習的過程中對這樣的設計結構有一些思考,它可以幫助你解決一些業(yè)務功能開發(fā)過程中的領域服務包裝。

四、實現(xiàn)

1. 工程結構

mybatis-step-02 └── src├── main│ └── java│ └── cn.bugstack.mybatis│ ├── binding│ │ ├── MapperProxy.java│ │ ├── MapperProxyFactory.java│ │ └── MapperRegistry.java│ └── session│ ├── defaults│ │ ├── DefaultSqlSession.java│ │ └── DefaultSqlSessionFactory.java│ ├── SqlSession.java│ └── SqlSessionFactory.java└── test└── java└── cn.bugstack.mybatis.test.dao├── dao│ ├── ISchoolDao.java│ └── IUserDao.java└── ApiTest.java

映射器標準定義實現(xiàn)關系,如圖 3-2

  • MapperRegistry 提供包路徑的掃描和映射器代理類注冊機服務,完成接口對象的代理類注冊處理。
  • SqlSession、DefaultSqlSession 用于定義執(zhí)行 SQL 標準、獲取映射器以及將來管理事務等方面的操作。基本我們平常使用 Mybatis 的 API 接口也都是從這個接口類定義的方法進行使用的。
  • SqlSessionFactory 是一個簡單工廠模式,用于提供 SqlSession 服務,屏蔽創(chuàng)建細節(jié),延遲創(chuàng)建過程。

2. 映射器注冊機

源碼詳見:cn.bugstack.mybatis.binding.MapperRegistry

public class MapperRegistry {/*** 將已添加的映射器代理加入到 HashMap*/private final Map, MapperProxyFactory> knownMappers = new HashMap<>();public T getMapper(Class type, SqlSession sqlSession) {final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);if (mapperProxyFactory == null) {throw new RuntimeException("Type " + type + " is not known to the MapperRegistry.");}try {return mapperProxyFactory.newInstance(sqlSession);} catch (Exception e) {throw new RuntimeException("Error getting mapper instance. Cause: " + e, e);}}public void addMapper(Class type) {/* Mapper 必須是接口才會注冊 */if (type.isInterface()) {if (hasMapper(type)) {// 如果重復添加了,報錯throw new RuntimeException("Type " + type + " is already known to the MapperRegistry.");}// 注冊映射器代理工廠knownMappers.put(type, new MapperProxyFactory<>(type));}}public void addMappers(String packageName) {Set> mapperSet = ClassScanner.scanPackage(packageName);for (Class mapperClass : mapperSet) {addMapper(mapperClass);}}}
  • MapperRegistry 映射器注冊類的核心主要在于提供了 ClassScanner.scanPackage 掃描包路徑,調(diào)用 addMapper 方法,給接口類創(chuàng)建 MapperProxyFactory 映射器代理類,并寫入到 knownMappers 的 HashMap 緩存中。
  • 另外就是這個類也提供了對應的 getMapper 獲取映射器代理類的方法,其實這步就包裝了我們上一章節(jié)手動操作實例化的過程,更加方便在 DefaultSqlSession 中獲取 Mapper 時進行使用。

3. SqlSession 標準定義和實現(xiàn)

源碼詳見:cn.bugstack.mybatis.session.SqlSession

public interface SqlSession {/*** Retrieve a single row mapped from the statement key* 根據(jù)指定的SqlID獲取一條記錄的封裝對象** @param the returned object type 封裝之后的對象類型* @param statement sqlID* @return Mapped object 封裝之后的對象*/T selectOne(String statement);/*** Retrieve a single row mapped from the statement key and parameter.* 根據(jù)指定的SqlID獲取一條記錄的封裝對象,只不過這個方法容許我們可以給sql傳遞一些參數(shù)* 一般在實際使用中,這個參數(shù)傳遞的是pojo,或者Map或者ImmutableMap** @param the returned object type* @param statement Unique identifier matching the statement to use.* @param parameter A parameter object to pass to the statement.* @return Mapped object*/T selectOne(String statement, Object parameter);/*** Retrieves a mapper.* 得到映射器,這個巧妙的使用了泛型,使得類型安全** @param the mapper type* @param type Mapper interface class* @return a mapper bound to this SqlSession*/T getMapper(Class type);}
  • 在 SqlSession 中定義用來執(zhí)行 SQL、獲取映射器對象以及后續(xù)管理事務操作的標準接口。
  • 目前這個接口中對于數(shù)據(jù)庫的操作僅僅只提供了 selectOne,后續(xù)還會有相應其他方法的定義。

源碼詳見:cn.bugstack.mybatis.session.defaults

public class DefaultSqlSession implements SqlSession {/*** 映射器注冊機*/private MapperRegistry mapperRegistry;@Overridepublic T selectOne(String statement, Object parameter) {return (T) ("你被代理了!" + "方法:" + statement + " 入?yún)?#xff1a;" + parameter);}@Overridepublic T getMapper(Class type) {return mapperRegistry.getMapper(type, this);}}
  • 通過 DefaultSqlSession 實現(xiàn)類對 SqlSession 接口進行實現(xiàn)。
  • getMapper 方法中獲取映射器對象是通過 MapperRegistry 類進行獲取的,后續(xù)這部分會被配置類進行替換。
  • 在 selectOne 中是一段簡單的內(nèi)容返回,目前還沒有與數(shù)據(jù)庫進行關聯(lián),這部分在我們漸進式的開發(fā)過程中逐步實現(xiàn)。

4. SqlSessionFactory 工廠定義和實現(xiàn)

源碼詳見:cn.bugstack.mybatis.session.SqlSessionFactory

public interface SqlSessionFactory {/*** 打開一個 session* @return SqlSession*/SqlSession openSession();}
  • 這其實就是一個簡單工廠的定義,在工廠中提供接口實現(xiàn)類的能力,也就是 SqlSessionFactory 工廠中提供的開啟 SqlSession 的能力。

源碼詳見:cn.bugstack.mybatis.session.defaults.DefaultSqlSessionFactory

public class DefaultSqlSessionFactory implements SqlSessionFactory {private final MapperRegistry mapperRegistry;public DefaultSqlSessionFactory(MapperRegistry mapperRegistry) {this.mapperRegistry = mapperRegistry;}@Overridepublic SqlSession openSession() {return new DefaultSqlSession(mapperRegistry);}}
  • 默認的簡單工廠實現(xiàn),處理開啟 SqlSession 時,對 DefaultSqlSession 的創(chuàng)建以及傳遞 mapperRegistry,這樣就可以在使用 SqlSession 時獲取每個代理類的映射器對象了。

五、測試

1. 事先準備

在同一個包路徑下,提供2個以上的 Dao 接口:

public interface ISchoolDao {String querySchoolName(String uId);}public interface IUserDao {String queryUserName(String uId);Integer queryUserAge(String uId);}

2. 單元測試

@Test public void test\_MapperProxyFactory() {// 1. 注冊 MapperMapperRegistry registry = new MapperRegistry();registry.addMappers("cn.bugstack.mybatis.test.dao");// 2. 從 SqlSession 工廠獲取 SessionSqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory(registry);SqlSession sqlSession = sqlSessionFactory.openSession();// 3. 獲取映射器對象IUserDao userDao = sqlSession.getMapper(IUserDao.class);// 4. 測試驗證String res = userDao.queryUserName("10001");logger.info("測試結果:{}", res); }
  • 在單元測試中通過注冊機掃描包路徑注冊映射器代理對象,并把注冊機傳遞給 SqlSessionFactory 工廠,這樣完成一個鏈接過程。
  • 之后通過 SqlSession 獲取對應 DAO 類型的實現(xiàn)類,并進行方法驗證。

測試結果

22:43:23.254 [main] INFO cn.bugstack.mybatis.test.ApiTest - 測試結果:你被代理了!方法:queryUserName 入?yún)?#xff1a;[Ljava.lang.Object;@50cbc42fProcess finished with exit code 0
  • 通過測試大家可以看到,目前我們已經(jīng)在一個有 Mybatis 影子的手寫 ORM 框架中,完成了代理類的注冊和使用過程。

六、總結

  • 首先要從設計結構上了解工廠模式對具體功能結構的封裝,屏蔽過程細節(jié),限定上下文關系,把對外的使用減少耦合。
  • 從這個過程上讀者伙伴也能發(fā)現(xiàn),使用 SqlSessionFactory 的工廠實現(xiàn)類包裝了 SqlSession 的標準定義實現(xiàn)類,并由 SqlSession 完成對映射器對象的注冊和使用。
  • 本章學習要注意幾個重要的知識點,包括:映射器、代理類、注冊機、接口標準、工廠模式、上下文。這些工程開發(fā)的技巧都是在手寫 Mybatis 的過程中非常重要的部分,了解和熟悉才能更好的在自己的業(yè)務中進行使用。

總結

以上是生活随笔為你收集整理的带码农《手写Mybatis》进度3:实现映射器的注册和使用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。