MyBatis的底層機制

手寫MyBatis底層機制

讀取配置文件,得到數據庫連接

思路

  1. 引入必要的依賴
  2. 需要寫一個自己的config.xml文件,在里面配置一些信息,driver,url ,password,username
  3. 需要編寫Configuration類,對 自己的config.xml文件 進行解析,得到一個數據庫連接

實現

  • 引入必要的依賴
<dependencies><dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.6.1</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.49</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.4</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency></dependencies>
  • 需要寫一個自己的config.xml文件,在里面配置一些信息,driver,url ,password,username
<?xml version="1.0" encoding="UTF-8" ?>
<database><property name="driverClassName" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/hsp_mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/><property name="username" value="root"/><property name="password" value="zy"/>
</database>
  • 需要編寫Configuration類,對 自己的config.xml文件 進行解析,得到一個數據庫連接
public class ZyConfiguration {//屬性 類加載器private ClassLoader classLoader =ClassLoader.getSystemClassLoader();//讀取xml文件信息并處理public Connection build(String resource) {Connection connection = null;//加載配置文件,獲取對應的InputStream流InputStream resourceAsStream =classLoader.getResourceAsStream(resource);//解析xml文件SAXReader reader = new SAXReader();try {Document document = reader.read(resourceAsStream);Element root = document.getRootElement();//解析rootElementSystem.out.println("root= "+root);return evalDataSource(root);} catch (DocumentException e) {throw new RuntimeException(e);}}//解析xml文件 并返回一個連接private Connection evalDataSource(Element node) {Iterator property = node.elementIterator("property");String driverClassName = null;String url = null;String username = null;String password = null;//遍歷node子節點 獲取屬性值while(property.hasNext()){Element pro = (Element)property.next();String name = pro.attributeValue("name");String value = pro.attributeValue("value");//判斷是否得到了name 和 valueif (name == null || value == null){throw new RuntimeException("property 節點沒有設置name 或 value屬性");}switch (name){case "driverClassName":driverClassName = value;break;case "url":url = value;break;case "username":username = value;break;case "password":password = value;break;default:throw new RuntimeException("屬性名沒有匹配到");}}Connection connection = null;try {Class.forName(driverClassName);connection = DriverManager.getConnection(url, username, password);} catch (Exception e) {throw new RuntimeException(e);}return connection;}}

編寫執行器,輸入SQL語句,完成操作

思路

  1. 需要寫一個實體類,對應monster表
  2. 編寫接口executor
  3. 實現接口,編寫自己的執行器
  4. 需要一個 自己的Configuration類 返回連接,通過連接對數據庫進行操作

實現

  • 需要寫一個實體類,對應monster表
@Setter
@Getter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Monster {private Integer id;private Integer age;private String name;private String email;private Date birthday;private double salary;private Integer gender;
}
  • 編寫接口executor
public interface Executor {public <T> T query(String statement,Object parameter);
}
  • 實現接口,編寫自己的執行器
public class ZyExecutor implements Executor{private ZyConfiguration zyConfiguration = new ZyConfiguration();@Overridepublic <T> T query(String sql, Object parameter) {Connection connection = getConnection();//查詢返回的結果集ResultSet set = null;PreparedStatement pre = null;try {pre = connection.prepareStatement(sql);//設置參數,如果參數多,用數組處理pre.setString(1, parameter.toString());set = pre.executeQuery();//把set數據封裝到對象 -- monsterMonster monster = new Monster();//簡化處理 認為返回的結果就是一個monster記錄//遍歷結果集while(set.next()){monster.setId(set.getInt("id"));monster.setName(set.getString("name"));monster.setEmail(set.getString("email"));monster.setAge(set.getInt("age"));monster.setGender(set.getInt("gender"));monster.setBirthday(set.getDate("birthday"));monster.setSalary(set.getDouble("salary"));}return (T)monster;} catch (SQLException e) {throw new RuntimeException(e);}finally {try {if (set != null) {set.close();}if (pre != null) {pre.close();}if (connection != null) {connection.close();}} catch (Exception e) {throw new RuntimeException(e);}}}public Connection getConnection(){//Configuration類 返回連接,通過連接對數據庫進行操作return zyConfiguration.build("zy_mybatis.xml");}
}
  • 需要一個 自己的Configuration類 返回連接,通過連接對數據庫進行操作

將Sqlsession封裝到執行器

思路

  1. 需要寫自己的Sqlsession類,它是搭建連接和執行器之間的橋梁,里面封裝有 執行器 和 配置文件 以及 操作DB 的具體方法
  2. 寫一個selectOne方法 ,SelectOne() 返回一條記錄,一條記錄對應一個Monster對象
實現
  • 需要寫自己的Sqlsession類,它是搭建連接和執行器之間的橋梁,里面封裝有 執行器 和 配置文件 以及 操作DB 的具體方法
public class ZySqlSession {//搭建連接和執行器之間的橋梁//執行器private Executor executor = new ZyExecutor();//配置private ZyConfiguration zyConfiguration = new ZyConfiguration();//操作DB 的具體方法//SelectOne 返回一條記錄-對象public <T> T selectOne(String statement,Object parameter){return executor.query(statement,parameter);}
}
  • 寫一個selectOne方法 ,SelectOne() 返回一條記錄,一條記錄對應一個Monster對象
//操作DB 的具體方法//SelectOne 返回一條記錄-對象public <T> T selectOne(String statement,Object parameter){return executor.query(statement,parameter);}

開發Mapper接口和Mapper.xml

思路

  1. 編寫MonsterMapper接口,里面有方法getMonsterById(Integer id)根據id返回一個monster對象
  2. 在resources下編寫對應的monsterMapper.xml(簡化:因為在resources 編譯時會在類路徑下比較好寫)
  3. monsterMapper.xml 編寫具體的sql語句,并指定語句類型,id,resultType(和原生Mybatis一樣)

實現

  • 編寫MonsterMapper接口,里面有方法getMonsterById(Integer id)根據id返回一個monster對象
public interface MonsterMapper {public Monster getMonsterById(Integer id);
}
  • 在resources下編寫對應的monsterMapper.xml(簡化:因為在resources 編譯時會在類路徑下比較好寫)
  • monsterMapper.xml 編寫具體的sql語句,并指定語句類型,id,resultType(和原生Mybatis一樣)
<?xml version="1.0" encoding="UTF-8" ?>
<mapper namespace="com.code_study.mapper.MonsterMapper"><!--    實現配置接口方法 getMonsterById--><select id="getMonsterById" resultType="com.code_study.entity.Monster">SELECT * FROM monster WHERE id = ?</select>
</mapper>

開發MapperBean,可以和Mapper接口相映射

思路

  1. 開發 Function類 ,用于記錄對應的Mapper的方法信息,比如sql類型,方法名,執行的sql語句,返回類型,入參類型
  2. 開發 MapperBean類,記錄接口信息和接口下的所有方法
  3. Function類 對應 monsterMapper.xml中的信息
  4. MapperBean類 對應 MonsterMapper 接口中的信息

實現

  • 開發 Function類 ,用于記錄對應的Mapper的方法信息,比如sql類型,方法名,執行的sql語句,返回類型,入參類型
//對應 monsterMapper.xml中的信息
public class Function {private String sqlType;//sql類型,比如select,insert,update,deleteprivate String funcName;//方法名private String sql;//執行的sql語句private Object resultType;//返回類型private String parameterType;//入參類型
}
  • 開發 MapperBean類,記錄接口信息和接口下的所有方法
//對應 MonsterMapper 接口中的信息
public class MapperBean {private String interfaceName;//接口名//    接口下的所有方法private List<Function> functions;
}
  • Function類 對應 monsterMapper.xml中的信息
  • MapperBean類 對應 MonsterMapper 接口中的信息

在Configuration中解析MapperXML獲取MapperBean對象

思路

  1. 在Configuration 添加方法readMapper(String path)
  2. 通過 path 讀取接口對應的Mapper方法
  3. 保存接口下所有的方法信息
  4. 封裝成 MapperBean對象

實現

  • 在Configuration 添加方法readMapper(String path)
  • 通過 path 讀取接口對應的Mapper方法
  • 保存接口下所有的方法信息
  • 封裝成 MapperBean對象
 //解析MapperXML獲取MapperBean對象//path = xml的路徑+文件名 是從類的加載路徑計算的(如果放在resource目錄下 之間傳xml文件名即可)public MapperBean readMapper(String path) {MapperBean mapperBean = new MapperBean();InputStream resourceAsStream = classLoader.getResourceAsStream(path);SAXReader reader = new SAXReader();try {Document document = reader.read(resourceAsStream);Element root = document.getRootElement();String namespace = root.attributeValue("namespace");mapperBean.setInterfaceName(namespace);List<Function> list = new ArrayList<>();//保存接口下所有的方法信息//得到root的迭代器Iterator iterator = root.elementIterator();while(iterator.hasNext()){Element e = (Element)iterator.next();String sqlType = e.getName().trim();String sql = e.getText().trim();String funcName = e.attributeValue("id");String resultType = e.attributeValue("resultType");//ResultType 返回的是一個Object對象 ->反射Object instance = Class.forName(resultType).newInstance();//封裝 function 對象Function function = new Function();function.setSql(sql);function.setSqlType(sqlType);function.setFuncName(funcName);function.setResultType(instance);//將封裝好的function對象 放入 list中list.add(function);mapperBean.setFunctions(list);}} catch (Exception e) {throw new RuntimeException(e);}return mapperBean;}

動態代理Mapper方法

思路

  1. 在SqlSession中添加方法 getMapper 輸入一個Class類型,返回mapper的動態代理對象
  2. 編寫動態代理類 實現 InvocationHandler 接口
  3. 取出mapperBean的functions 遍歷
  4. 判斷 當前要執行的方法和function.getFunctionName是否一致
  5. 調用方法返回 動態代理對象
  6. 編寫SqlSessionFactory 會話工廠,可以返回SqlSession

實現

  • 編寫動態代理類 實現 InvocationHandler 接口

  • 在SqlSession中添加方法 getMapper 輸入一個Class類型,返回mapper的動態代理對象

 //返回mapper的動態代理對象public <T> T getMapper(Class<T> clazz){return (T) Proxy.newProxyInstance(clazz.getClassLoader(),new Class[]{clazz},new ZyMapperProxy(zyConfiguration,clazz,this));}
  • 取出mapperBean的functions 遍歷
  • 判斷 當前要執行的方法和function.getFunctionName是否一致
  • 調用方法返回 動態代理對象
public class ZyMapperProxy implements InvocationHandler {private ZySqlSession zySqlSession;private String mapperFile;private ZyConfiguration zyConfiguration;public ZyMapperProxy(ZySqlSession zySqlSession, Class clazz, ZyConfiguration zyConfiguration) {this.zySqlSession = zySqlSession;this.zyConfiguration = zyConfiguration;this.mapperFile = clazz.getSimpleName() + ".xml";}//當執行Mapper接口的代理對象方法時,會執行到invoke方法@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {MapperBean mapperBean = zyConfiguration.readMapper(this.mapperFile);//判斷是否為當前xml文件對應的接口if (!method.getDeclaringClass().getName().equals(mapperBean.getInterfaceName())){return null;}//取出mapperBean的functionsList<Function> functions = mapperBean.getFunctions();//判斷當前的mapperBean 解析對應的MapperXML后,有方法if (null != functions || 0 != functions.size()){for (Function function : functions) {//當前要執行的方法和function.getFunctionNameif (method.getName().equals(function.getFuncName())){if ("SELECT".equalsIgnoreCase(function.getSqlType())){return zySqlSession.selectOne(function.getSql(),String.valueOf(args[0]));}}}}return null;}
}
  • 編寫SqlSessionFactory 會話工廠,可以返回SqlSession
public class ZySqlSessionFactory {public static ZySqlSession open(){return new ZySqlSession();}
}

測試

@Test
public void openSession(){ZySqlSession zySqlSession = ZySqlSessionFactory.openSession();System.out.println("zySqlSession= "+zySqlSession);MonsterMapper mapper = zySqlSession.getMapper(MonsterMapper.class);Monster monster = mapper.getMonsterById(1);System.out.println("monster= "+monster);
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/43016.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/43016.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/43016.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

aosp 單獨grep某種類型文件,加快grep速度。

1、問題 source build/envsetup.sh lunch xxx 后可以 mgrep 可以單獨搜索makefile文件 cgrep 可以單獨搜索c/c文件 jgrep 可以單獨搜索java文件 具體可以查看build/envsetup.sh cat build/envsetup.sh function jgrep() {find . -name .repo -prune -o -name .git -prune -o …

我“硬剛”mmkv開源庫對于版本號的定義贏啦!

我“硬剛”mmkv開源庫勝利啦&#xff01; 前情是這個帖子https://blog.csdn.net/jzlhll123/article/details/139917169 之前項目中將mmkv1.3.4升級到1.3.5或者1.3.6&#xff0c;就從firebase后臺上看到crash。 java.lang.UnsatisfiedLinkError: dlopen failed: library “libmm…

C#面:闡述什么是依賴注入?

依賴注入&#xff08;Dependency Injection&#xff0c;簡稱DI&#xff09;是一種設計模式&#xff0c;用于解耦組件之間的依賴關系。在傳統的編程模式中&#xff0c;一個對象通常會直接創建和管理它所依賴的其他對象。而在依賴注入中&#xff0c;對象不再負責創建和管理它所依…

申請EV代碼簽名證書費用是多少?

代碼簽名證書是確保軟件安全性和可信度的關鍵工具&#xff0c;在軟件開發領域扮演著至關重要的角色。EV代碼簽名證書&#xff0c;即擴展驗證代碼簽名證書&#xff0c;以其最高級別的安全性和信任度&#xff0c;成為大型企業或對安全性要求較高的軟件的首選。本文旨在深入探討EV…

2024最新版若依-RuoYi-Vue3-PostgreSQL前后端分離項目部署手冊教程

項目簡介: RuoYi-Vue3-PostgreSQL 是一個基于 RuoYi-Vue3 框架并集成 PostgreSQL 數據庫的項目。該項目提供了一套高效的前后端分離的開發解決方案&#xff0c;適用于中小型企業快速構建現代化的企業級應用。此項目結合了 RuoYi-Vue-Postgresql 和 RuoYi-Vue3 的優點&#xff0…

07.C2W2.Part-of-Speech (POS) Tagging and Hidden Markov Models

往期文章請點這里 目錄 OverviewPart of Speech TaggingMarkov ChainsMarkov Chains and POS TagsPOS tags as StatesTransition probabilitiesThe transition matrixInitial probabilities Hidden Markov ModelsEmission probabilitiesSummary Calculating ProbabilitiesTran…

全志A527 T527 設置左右分屏修改為單屏幕,應用分屏改為單屏

1.前言 android13中,A527的系統設置變成,左邊是一級菜單,右側是二級菜單, 這樣跟我們以前android7/8/9的布局是不一樣的,我們需要將它修改為一級菜單,點進去才是二級菜單這種。 效果如下 2.系統設置實現分析 它這里使用的是google新出的embedding activity, 相關的知…

LabVIEW中自定義Ring控件的圖標

在LabVIEW中&#xff0c;自定義Ring控件的圖標可以讓用戶界面更加直觀和友好。以下是如何在LabVIEW中自定義Ring控件的圖標的詳細步驟&#xff1a; 步驟1&#xff1a;創建或獲取圖標 首先&#xff0c;你需要創建或獲取你想要在Ring控件中使用的圖標。你可以使用圖像編輯軟件&…

Docker拉取失敗,利用github將鏡像推送到阿里云

背景 由于近期國內docker鏡像地址失效&#xff08;2024年6月份開始&#xff09;&#xff0c;導致pull docker 鏡像總是超時。 涉及到的網址和工具 https://github.com/tech-shrimp/docker_image_pusherhttps://hub.docker.com/search阿里云 GITHUB配置 fork https://githu…

【C++初階】與C相比,C++多出來簡單又好用的語法(命名空間、輸入輸出、缺省參數、函數重載)

文章目錄 一、 C的第一個代碼1、C兼容C語言2、hello world 如何用C語法來寫 二、命名空間namespace1、為什么有命名空間2、定義3、命名空間的使用4、注意事項 三、輸入輸出四、缺省參數1、定義2、注意 五、函數重載1、定義2、使用 六、謝謝觀看&#xff01; 一、 C的第一個代碼…

go mod 依賴管理補充2

依賴包的版本問題&#xff0c;別的開發語言有沒有類似的問題&#xff1f;是怎么解決的&#xff1f; 舉例&#xff1a;java java的依賴包的版本問題&#xff0c;通過Maven模塊來操作&#xff0c;可以指定依賴包版本號&#xff0c;如下&#xff1a; go.mod 文件 go.mod文件是G…

怎么用 matlab 設計滯后-超前串聯校正網絡

&#x1f3c6;本文收錄于「Bug調優」專欄&#xff0c;主要記錄項目實戰過程中的Bug之前因后果及提供真實有效的解決方案&#xff0c;希望能夠助你一臂之力&#xff0c;幫你早日登頂實現財富自由&#x1f680;&#xff1b;同時&#xff0c;歡迎大家關注&&收藏&&…

Java多態的理解

目錄 一、什么是多態 二、多態實現的條件 三、實例分析 四、多態應用之一&#xff08;多態數組&#xff09; 五、多態參數 一、什么是多態 在Java中&#xff0c;多態是面向對象編程中的一個重要概念&#xff0c;它允許不同類型的對象對同一方法進行不同的實現。具體來說&…

Kaggle網站免費算力使用,深度學習模型訓練

聲明&#xff1a; 本文主要內容為&#xff1a;kaggle網站數據集上傳&#xff0c;訓練模型下載、模型部署、提交后臺運行等教程。 1、賬號注冊 此步驟本文略過&#xff0c;如有需要可以參考其他文章。 2、上傳資源 不論是上傳訓練好的模型進行預測&#xff0c;還是訓練用的…

如何提升美國Facebook直播的整體體驗?

Facebook作為全球最大的社交媒體平臺之一&#xff0c;提供了直播功能&#xff0c;用戶可以實時分享生活、見解和創意。許多商家通過美國Facebook直播來獲取更多客戶&#xff0c;但直播時可能會遇到網絡卡頓的問題&#xff0c;導致觀看體驗不佳。本文將探討如何解決這個問題&…

文華財經盤立方期貨通鱷魚指標公式均線交易策略源碼

文華財經盤立方期貨通鱷魚指標公式均線交易策略源碼&#xff1a; 新建主圖幅圖類型指標都可以&#xff01; VAR1:(HL)/2; 唇:REF(SMA(VAR1,5,1),3),COLORGREEN; 齒:REF(SMA(VAR1,8,1),5),COLORRED; 顎:REF(SMA(VAR1,13,1),8),COLORBLUE;

C++規范

一、VS工具集列表&#xff1a; Visual Studio 2008&#xff1a;v90 Visual Studio 2010&#xff1a;v100 Visual Studio 2012&#xff1a;v110 Visual Studio 2013&#xff1a;v120 Visual Studio 2015&#xff1a;v140 &#xff08;v140_xp&#xff09; Visual Studio 2017&a…

FinClip SDK 入駐鴻蒙生態伙伴市場,激發鴻蒙應用創新活力

華為近期宣布開放“鴻蒙生態伙伴SDK市場”&#xff0c;甄選各類優質、安全的SDK加入聚合平臺&#xff0c;致力于幫助各行業開發者輕松、高效地打造鴻蒙原生應用。 目前&#xff0c;已有18個領域、超過350個SDK適配HarmonyOS NEXT版本&#xff0c;并有超過120個SDK完成上架發布…

【Linux Git入門】Git的介紹

文章目錄 前言git簡介git是什么git的作用為什么要學習git安裝git總結前言 在現代軟件開發中,版本控制系統已經成為了不可或缺的工具。其中,Git是最受歡迎的版本控制系統之一。Git是由Linux的創造者Linus Torvalds在2005年創建的,用于管理Linux內核的開發。Git是一個分布式版…

const 修飾不同內容區分

1.修飾局部變量 const int a 1;int const a 1; 這兩種是一樣的 注意&#xff1a; const int b; 該情況下編譯器會報錯&#xff1a;常量變量"b”需要初始值設定項 將一個變量沒有賦初始值直接const修飾后&#xff0c;在以后時無法更改內容的。 2.修飾常量字符串 a.…