<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          自己動手實(shí)現(xiàn)一個(gè)ORM框架

          共 10252字,需瀏覽 21分鐘

           ·

          2022-05-27 04:29

          點(diǎn)擊關(guān)注公眾號,Java干貨及時(shí)送達(dá)

          作者 |?汪偉俊?

          出品 | Java技術(shù)迷(ID:JavaFans1024)

          引言

          本篇文章我們來自己動手實(shí)現(xiàn)一個(gè)ORM框架,我們先來看一下傳統(tǒng)的JDBC代碼:

          static?final?String?JDBC_DRIVER?=?"com.mysql.jdbc.Driver";??
          static?final?String?JDBC_URL?=?"jdbc:mysql:///user";
          static?final?String?USER_NAME?=?"root";
          static?final?String?PASS_WORD?=?"123456";

          public?static?void?main(String[]?args)?{
          ????Class.forName(JDBC_DRIVER);
          ????Connection?conn?=?DriverManager.getConnection(JDBC_URL,?USER_NAME,?PASS_WORD);
          ????Statement?stmt?=?conn.createStatement();
          ????String?sql?=?"SELECT?*?FROM?user";
          ????ResultSet?rs?=?stmt.executeQuery(sql);
          ????while(rs.next()){
          ????????int?id??=?rs.getInt("id");
          ????????int?age?=?rs.getInt("age");
          ????????System.out.println("ID:?"?+?id);
          ????????System.out.println("Age:?"?+?age);
          ????}
          ????rs.close();
          }

          以上代碼通過JDBC實(shí)現(xiàn)了對數(shù)據(jù)表的查詢操作,不過這里有一些明顯的問題,對于數(shù)據(jù)庫的配置信息是硬編碼在代碼中的,想要修改配置信息還得來修改代碼,我們可以將其抽取成一個(gè)配置文件;對于sql的編寫也是硬編碼在代碼中,也可以考慮將其抽取出去;然后是對結(jié)果集的封裝,每次都需要通過循環(huán)解析結(jié)果集也非常麻煩。綜上所述,我們借鑒MyBatis來實(shí)現(xiàn)一個(gè)自己的ORM框架。

          ORM框架整體架構(gòu)

          我們先來梳理一下框架的整體架構(gòu),首先我們需要解析一下配置文件,正如MyBatis框架那樣,我們需要使用到兩種配置文件,一個(gè)是框架的全局配置文件,一個(gè)是Mapper配置文件,定義格式如下:

          <configuration>
          configuration>
          <mapper>
          mapper>

          那么首先框架的第一步就是讀取配置文件,全局配置文件中應(yīng)該包含數(shù)據(jù)源配置信息和Mapper配置文件所在位置,如下所示:

          <configuration>
          ????
          ????<dataSource>
          ????????<property?name="driverClass"?value="com.mysql.jdbc.Driver"/>
          ????????<property?name="jdbcUrl"?value="jdbc:mysql:///user"/>
          ????????<property?name="username"?value="root"/>
          ????????<property?name="password"?value="123456"/>
          ????dataSource>

          ????
          ????<mapper?resource="UserMapper.xml"/>
          configuration>

          對該配置文件進(jìn)行解析后,我們可以將這些數(shù)據(jù)封裝成一個(gè)Java實(shí)體,該實(shí)體包含了所有的配置信息,由于全局配置文件中可能含有多個(gè)Mapper文件的配置,所以將其封裝成一個(gè)Map集合:

          Map

          集合的key為String類型,value為MapperStatement類型,MapperStatement是對Mapper配置文件的一個(gè)封裝:

          <mapper?namespace="user">
          ????<select?id="selectList"?resultType="com.wwj.pojo.User">
          ????????select?*?from?e_user
          ????select>
          mapper>

          這里需要注意一點(diǎn),框架會將整個(gè)項(xiàng)目中的Mapper配置文件都封裝成一個(gè)MapperStatement并保存到Map中,這就需要對每個(gè)MapperStatement進(jìn)行區(qū)分,區(qū)分的關(guān)鍵就是Mapper配置文件中的namespaceid,我們將其拼接起來作為statementId。到這里,配置文件的解析就完成了,然后我們提供對應(yīng)的查詢方法,該查詢方法的作用是對sql語句進(jìn)行解析并調(diào)用JDBC查詢數(shù)據(jù)庫,通過內(nèi)省封裝結(jié)果集。以上是框架的一個(gè)整體思路,大家可能現(xiàn)在還沒有理解到,沒關(guān)系,接下來是對實(shí)現(xiàn)過程的一個(gè)詳細(xì)概述。

          解析配置文件

          新建一個(gè)類Resources,該類負(fù)責(zé)將一個(gè)文件轉(zhuǎn)換成輸入流:

          public?class?Resources?{

          ????/**
          ?????*?根據(jù)配置文件的路徑將配置文件加載成字節(jié)輸入流
          ?????*
          ?????*?@param?path
          ?????*?@return
          ?????*/

          ????public?static?InputStream?getResourceAsStream(String?path)?{
          ????????return?Resources.class.getClassLoader().getResourceAsStream(path);
          ????}
          }

          接下來我們需要一個(gè)SqlSessionFactoryBuilder對象,該對象會提供一個(gè)build方法來生成SqlSessionFactory:

          public?class?SqlSessionFactoryBuilder?{

          ????public?SqlSessionFactory?build(InputStream?inputStream)?throws?DocumentException,?PropertyVetoException?{
          ????????//?使用dom4j解析配置文件,將解析出來的內(nèi)容封裝到Configuration中
          ????????XmlConfigBuilder?builder?=?new?XmlConfigBuilder();
          ????????Configuration?configuration?=?builder.parseConfig(inputStream);
          ????????//?創(chuàng)建SqlSessionFactory對象
          ????????SqlSessionFactory?sqlSessionFactory?=?new?DefaultSqlSessionFactory(configuration);
          ????????return?sqlSessionFactory;
          ????}
          }

          SqlSessionFactory是一個(gè)接口,我們創(chuàng)建它的默認(rèn)實(shí)現(xiàn)類DefaultSqlSessionFactory,該類需要傳入一個(gè)Configuration類型對象,這個(gè)Configuration就是對全局配置文件的一個(gè)封裝:

          public?class?Configuration?{

          ????private?DataSource?dataSource;
          ????/**
          ?????*? key:statementId
          ?????*? value:封裝好的MapperStatement對象
          ?????*/

          ????private?Map?mappedStatementMap?=?new?HashMap<>();
          }

          那么現(xiàn)在的關(guān)鍵就是對全局配置文件的解析了,我們提供一個(gè)類XmlConfigBuilder,該類的parseConfig方法可以將輸入流轉(zhuǎn)換為Configuration對象,實(shí)現(xiàn)如下:

          public?Configuration?parseConfig(InputStream?inputStream)?throws?DocumentException,?PropertyVetoException?{
          ????????Document?document?=?new?SAXReader().read(inputStream);
          ????????//?
          ????????Element?rootElement?=?document.getRootElement();
          ????????//?全局查找標(biāo)簽
          ????????List?propertyList?=?rootElement.selectNodes("http://property");
          ????????Properties?properties?=?new?Properties();
          ????????propertyList.forEach(element?->?{
          ????????????//?獲取到標(biāo)簽中的name和value屬性
          ????????????String?name?=?element.attributeValue("name");
          ????????????String?value?=?element.attributeValue("value");
          ????????????properties.setProperty(name,?value);
          ????????});
          ????????//?創(chuàng)建數(shù)據(jù)源
          ????????ComboPooledDataSource?comboPooledDataSource?=?new?ComboPooledDataSource();
          ????????comboPooledDataSource.setDriverClass(properties.getProperty("driverClass"));
          ????????comboPooledDataSource.setJdbcUrl(properties.getProperty("jdbcUrl"));
          ????????comboPooledDataSource.setUser(properties.getProperty("username"));
          ????????comboPooledDataSource.setPassword(properties.getProperty("password"));

          ????????configuration.setDataSource(comboPooledDataSource);

          ????????//?解析mapper.xml文件
          ????????List?mapperList?=?rootElement.selectNodes("http://mapper");
          ????????for?(Element?element?:?mapperList)?{
          ????????????String?mapperPath?=?element.attributeValue("resource");
          ????????????InputStream?mapperAsStream?=?Resources.getResourceAsStream(mapperPath);
          ????????????XmlMapperBuilder?xmlMapperBuilder?=?new?XmlMapperBuilder(configuration);
          ????????????xmlMapperBuilder.parse(mapperAsStream);
          ????????}
          ????????return?configuration;
          ????}

          借助dom4j可以很容易地實(shí)現(xiàn)解析,將每個(gè)標(biāo)簽中的屬性和屬性值讀取出來,進(jìn)行對應(yīng)的封裝即可,對于Mapper配置文件的解析也是如此,通過resource屬性可以得到Mapper文件位置,然后將其轉(zhuǎn)為輸入流并解析:

          public?class?XmlMapperBuilder?{

          ????private?Configuration?configuration;

          ????public?XmlMapperBuilder(Configuration?configuration)?{
          ????????this.configuration?=?configuration;
          ????}

          ????public?void?parse(InputStream?inputStream)?throws?DocumentException?{
          ????????Document?document?=?new?SAXReader().read(inputStream);
          ????????//?得到根標(biāo)簽
          ????????Element?rootElement?=?document.getRootElement();
          ????????String?namespace?=?rootElement.attributeValue("namespace");
          ????????//?得到所有
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  琪琪先锋 torrent magnet 国产精品久久久久久久久久久久久免费看 | 91视频人人爱 | 超碰人人人人 | 青青青草成人视频视频 | 国产免费污污 |