<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>

          長(zhǎng)文干貨 | 手寫(xiě)自定義持久層框架!

          共 11385字,需瀏覽 23分鐘

           ·

          2021-01-14 14:28



          Baoxing

          讀完需要

          52
          分鐘

          速讀僅需 38 分鐘

          為何要手寫(xiě)自定義持久層框架?

          1. JDBC 編碼的弊端
          • 會(huì)造成硬編碼問(wèn)題(無(wú)法靈活切換數(shù)據(jù)庫(kù)驅(qū)動(dòng)) 頻繁創(chuàng)建和釋放數(shù)據(jù)庫(kù)連接造成系統(tǒng)資源浪費(fèi) 影響系統(tǒng)性能
          • sql 語(yǔ)句存在硬編碼,造成代碼不易維護(hù),實(shí)際應(yīng)用中 sql 變化可能較大,變動(dòng) sql 需要改 Java 代碼
          • 使用 preparedStatement 向占有位符號(hào)傳參數(shù)存在硬編碼, 因 sql 語(yǔ)句的 where 條件不確定甚至沒(méi)有where條件,修改 sql 還要修改代碼 系統(tǒng)不易維護(hù)
          • 對(duì)結(jié)果集解析也存在硬編碼, sql變化導(dǎo)致解析代碼變化
          1. 更有助于讀 mybatis 持久層框架源碼

          JDBC代碼

          public?class?jdbcConnection?{
          ????private?static?Connection?connection?=?null;
          ????private?static?PreparedStatement?preparedStatement?=?null;
          ????private?static?ResultSet?resultSet?=?null;

          ????public?static?void?main(String[]?args)?{
          ????????try?{
          ????????????//?加載數(shù)據(jù)庫(kù)驅(qū)動(dòng)
          ????????????Class.forName("com.mysql.jdbc.Driver");
          ????????????//?通過(guò)驅(qū)動(dòng)管理類(lèi)獲取數(shù)據(jù)庫(kù)連接
          ????????????connection?=?DriverManager.getConnection("jdbc:mysql://localhost:3306/huodd",?"root",?"1234");
          ????????????//?定義sql語(yǔ)句???表示占位符
          ????????????String?sql?=?"select?id,username?from?user?where?id?=??";
          ????????????//?獲取預(yù)處理對(duì)象?statement
          ????????????PreparedStatement?preparedStatement?=?(PreparedStatement)?connection.prepareStatement(sql);
          ????????????//?設(shè)置參數(shù)?第一個(gè)參數(shù)為?sql?語(yǔ)句中參數(shù)的序號(hào)(從1開(kāi)始)?第二個(gè)參數(shù)為?設(shè)置的參數(shù)值
          ????????????preparedStatement.setInt(1,?1);
          ????????????//?向數(shù)據(jù)庫(kù)發(fā)出sql執(zhí)行查詢?查詢出結(jié)果集
          ????????????resultSet?=?preparedStatement.executeQuery();
          ????????????//?遍歷查詢結(jié)果集
          ????????????while?(resultSet.next())?{
          ????????????????int?id?=?resultSet.getInt("id");
          ????????????????String?username?=?resultSet.getString("username");
          ????????????????//?封裝對(duì)象
          ????????????????User?user?=?new?User();
          ????????????????user.setId(id);
          ????????????????user.setUsername(username);
          ????????????????System.out.println(user);
          ????????????}
          ????????}?catch?(Exception?ex)?{
          ????????????ex.printStackTrace();
          ????????}?finally?{
          ????????????try?{
          ????????????????//?釋放資源
          ????????????????if?(resultSet?!=?null)?{
          ????????????????????resultSet.close();
          ????????????????}
          ????????????????if?(preparedStatement?!=?null)?{
          ????????????????????preparedStatement.close();
          ????????????????}
          ????????????????if?(connection?!=?null)?{
          ????????????????????connection.close();
          ????????????????}
          ????????????}?catch?(Exception?ex)?{
          ????????????????ex.printStackTrace();
          ????????????}
          ????????}
          ????}
          }

          解決問(wèn)題的思路

          1. 數(shù)據(jù)庫(kù)頻繁創(chuàng)建連接、釋放資源 -> 連接池
          2. sql語(yǔ)句及參數(shù)硬編碼 -> 配置文件
          3. 手動(dòng)解析封裝結(jié)果集 -> 反射、內(nèi)省

          編碼前思路整理

          1. 創(chuàng)建、讀取配置文件
          • sqlMapConfig.xml 存放數(shù)據(jù)庫(kù)配置信息
          • userMapper.xml :存放sql配置信息
          • 根據(jù)配置文件的路徑,加載配置文件成字節(jié)輸入流,存儲(chǔ)在內(nèi)存中Resources#getResourceAsStream(String path)
          • 創(chuàng)建兩個(gè)JavaBean存儲(chǔ)配置文件解析出來(lái)的內(nèi)容
            1. Configuration :核心配置類(lèi) ,存放 sqlMapConfig.xml解析出來(lái)的內(nèi)容
            2. MappedStatement:映射配置類(lèi):存放mapper.xml解析出來(lái)的內(nèi)容
          1. 解析配置文件(使用dom4j)
          • 創(chuàng)建類(lèi):SqlSessionFactoryBuilder#build(InputStream in) ? -> 設(shè)計(jì)模式之構(gòu)建者模式
          • 使用dom4j解析配置文件,將解析出來(lái)的內(nèi)容封裝到容器對(duì)象(JavaBean)中
          1. 創(chuàng)建 SqlSessionFactory 接口及實(shí)現(xiàn)類(lèi)DefaultSqlSessionFactory
          • SqlSessionFactory對(duì)象,生產(chǎn)sqlSession會(huì)話對(duì)象 ?-> 設(shè)計(jì)模式之工廠模式
          1. 創(chuàng)建 SqlSession接口及實(shí)現(xiàn)類(lèi)DefaultSqlSession
          • 定義對(duì)數(shù)據(jù)庫(kù)的CRUD操作
            1. selectList()
            2. selectOne()
            3. update()
            4. delete()
          1. 創(chuàng)建Executor接口及實(shí)現(xiàn)類(lèi)SimpleExecutor實(shí)現(xiàn)類(lèi)
          • query(Configuration configuration, MappedStatement mapStatement, Object... orgs) ?執(zhí)行的就是JDBC代碼
          1. 測(cè)試代碼

          用到的設(shè)計(jì)模式

          • 構(gòu)建者模式
          • 工廠模式
          • 代理模式

          進(jìn)入編碼

          1.創(chuàng)建、讀取配置文件

          sqlMapConfig.xml 存放數(shù)據(jù)庫(kù)配置信息

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

          ????
          ????<mapper?resource="userMapper.xml">mapper>

          configuration>

          userMapper.xml 存放sql配置信息

          <mapper?namespace="user">

          ????
          ????<select?id="selectList"?resultType="com.huodd.pojo.User"?paramterType="com.huodd.pojo.User">
          ????????select?*?from?user
          ????select>

          ????<select?id="selectOne"?paramterType="com.huodd.pojo.User"?resultType="com.huodd.pojo.User">
          ????????select?*?from?user?where?id?=?#{id}?and?username?=#{username}
          ????select>

          mapper>

          User.java

          public?class?User?{
          ????private?Integer?id;
          ????private?String?username;

          ????...?省略getter?setter?方法
          ????...?省略?toString?方法
          }

          pom.xml 中引入依賴(lài)

          <dependency>
          ????<groupId>mysqlgroupId>
          ????<artifactId>mysql-connector-javaartifactId>
          ????<version>5.1.17version>
          dependency>
          <dependency>
          ????<groupId>c3p0groupId>
          ????<artifactId>c3p0artifactId>
          ????<version>0.9.1.2version>
          dependency>
          <dependency>
          ????<groupId>log4jgroupId>
          ????<artifactId>log4jartifactId>
          ????<version>1.2.12version>
          dependency>
          <dependency>
          ????<groupId>junitgroupId>
          ????<artifactId>junitartifactId>
          ????<version>4.10version>
          dependency>
          <dependency>
          ????<groupId>dom4jgroupId>
          ????<artifactId>dom4jartifactId>
          ????<version>1.6.1version>
          dependency>
          <dependency>
          ????<groupId>jaxengroupId>
          ????<artifactId>jaxenartifactId>
          ????<version>1.1.6version>
          dependency>

          創(chuàng)建兩個(gè)JavaBean對(duì)象 用于存儲(chǔ)解析的配置文件的內(nèi)容(Configuration.java、MappedStatement.java)

          public?class?Configuration?{

          ????//?數(shù)據(jù)源
          ????private?DataSource?dataSource;
          ????//map集合?key:statementId?value:MappedStatement
          ????private?Map?mappedStatementMap?=?new?HashMap<>();

          ????...?省略getter?setter?方法
          }

          public?class?MappedStatement?{

          ????//?id
          ????private?String?id;
          ????//?sql?語(yǔ)句
          ????private?String?sql;
          ????//?參數(shù)值類(lèi)型
          ????private?Class?paramterType;
          ????//?返回值類(lèi)型
          ????private?Class?resultType;

          ???...?省略getter?setter?方法
          }

          創(chuàng)建Resources工具類(lèi) 并編寫(xiě)靜態(tài)方法getResourceAsSteam(String path)

          public?class?Resources?{

          ????/**
          ?????*?根據(jù)配置文件的路徑?將配置文件加載成字節(jié)輸入流?存儲(chǔ)在內(nèi)存中
          ?????*?@param?path
          ?????*?@return?InputStream
          ?????*/

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

          2.解析配置文件(使用dom4j)

          創(chuàng)建 SqlSessionFactoryBuilder類(lèi) 并添加 build 方法

          public?class?SqlSessionFactoryBuilder?{

          ????public?SqlSessionFactory?build?(InputStream?in)?throws?DocumentException,?PropertyVetoException,?ClassNotFoundException?{
          ????????//?1.?使用?dom4j?解析配置文件?將解析出來(lái)的內(nèi)容封裝到Configuration中
          ????????XMLConfigerBuilder?xmlConfigerBuilder?=?new?XMLConfigerBuilder();

          ????????//?configuration?是已經(jīng)封裝好了sql信息和數(shù)據(jù)庫(kù)信息的對(duì)象
          ????????Configuration?configuration?=?xmlConfigerBuilder.parseConfig(in);

          ????????//?2.?創(chuàng)建?SqlSessionFactory?對(duì)象??工廠類(lèi)?主要是生產(chǎn)sqlSession會(huì)話對(duì)象
          ????????DefaultSqlSessionFactory?defaultSqlSessionFactory?=?new?DefaultSqlSessionFactory(configuration);

          ????????return?defaultSqlSessionFactory;
          ????}
          }
          public?class?XMLConfigerBuilder?{

          ????private?Configuration?configuration;

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

          ????/**
          ?????*?該方法?使用dom4j對(duì)配置文件進(jìn)行解析?封裝Configuration
          ?????*?@param?in
          ?????*?@return
          ?????*/

          ?????public?Configuration?parseConfig?(InputStream?in)?throws?DocumentException,?PropertyVetoException,?ClassNotFoundException?{
          ?????????Document?document?=?new?SAXReader().read(in);
          ?????????//?
          ?????????Element?rootElement?=?document.getRootElement();
          ?????????List?propertyElements?=?rootElement.selectNodes("http://property");
          ?????????Properties?properties?=?new?Properties();
          ?????????for?(Element?propertyElement?:?propertyElements)?{
          ?????????????properties.setProperty(propertyElement.attributeValue("name"),?propertyElement.attributeValue("value"));
          ?????????}
          ?????????//?連接池
          ?????????ComboPooledDataSource?comboPooledDataSource?=?new?ComboPooledDataSource();
          ?????????comboPooledDataSource.setDriverClass(properties.getProperty("driverClass"));
          ?????????comboPooledDataSource.setJdbcUrl(properties.getProperty("jdbcUrl"));
          ?????????comboPooledDataSource.setUser(properties.getProperty("user"));
          ?????????comboPooledDataSource.setPassword(properties.getProperty("password"));

          ?????????//?填充?configuration
          ?????????configuration.setDataSource(comboPooledDataSource);

          ?????????//?mapper?部分??拿到路徑?->?字節(jié)輸入流?->?dom4j進(jìn)行解析
          ?????????List?mapperElements?=?rootElement.selectNodes("http://mapper");
          ?????????XMLMapperBuilder?xmlMapperBuilder?=?new?XMLMapperBuilder(configuration);
          ?????????for?(Element?mapperElement?:?mapperElements)?{
          ?????????????String?mapperPath?=?mapperElement.attributeValue("resource");
          ?????????????InputStream?resourceAsStream?=?Resources.getResourceAsStream(mapperPath);
          ?????????????xmlMapperBuilder.parse(resourceAsStream);
          ?????????}

          ?????????return?configuration;
          ?????}
          }
          public?class?XMLMapperBuilder?{

          ????private?Configuration?configuration;

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

          ????public?void?parse(InputStream?inputStream)?throws?DocumentException,?ClassNotFoundException?{
          ????????Document?document?=?new?SAXReader().read(inputStream);
          ????????//?
          ????????Element?rootElement?=?document.getRootElement();
          ????????String?namespace?=?rootElement.attributeValue("namespace");
          ????????List?select?=?rootElement.selectNodes("http://select");
          ????????for?(Element?element?:?select)?{
          ????????????//?獲取?id?的值
          ????????????String?id?=?element.attributeValue("id");
          ????????????String?paramterType?=?element.attributeValue("paramterType");
          ????????????String?resultType?=?element.attributeValue("resultType");
          ????????????//?輸入?yún)?shù)?class
          ????????????Class?paramterTypeClass?=?getClassType(paramterType);
          ????????????//?返回結(jié)果?class
          ????????????Class?resultTypeClass?=?getClassType(resultType);
          ????????????//?sql?語(yǔ)句
          ????????????String?sqlStr?=?element.getTextTrim();

          ????????????//?封裝?mappedStatement
          ????????????MappedStatement?mappedStatement?=?new?MappedStatement();
          ????????????mappedStatement.setId(id);
          ????????????mappedStatement.setParamterType(paramterTypeClass);
          ????????????mappedStatement.setResultType(resultTypeClass);
          ????????????mappedStatement.setSql(sqlStr);

          ????????????//?statementId
          ????????????String?key?=?namespace?+?"."?+?id;
          ????????????//?填充?configuration
          ????????????configuration.getMappedStatementMap().put(key,?mappedStatement);
          ????????}

          ????}

          ????private?Class?getClassType(String?paramterType)?throws?ClassNotFoundException?{
          ????????Class?aClass?=?Class.forName(paramterType);
          ????????return?aClass;
          ????}
          }

          3.創(chuàng)建 SqlSessionFactory 接口及實(shí)現(xiàn)類(lèi)DefaultSqlSessionFactory

          public?interface?SqlSessionFactory?{
          ????SqlSession?openSession();
          }
          public?class?DefaultSqlSessionFactory?implements?SqlSessionFactory?{

          ????private?Configuration?configuration;

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

          ????@Override
          ????public?SqlSession?openSession()?{
          ????????return?new?DefaultSqlSession(configuration);
          ????}
          }

          4. 創(chuàng)建 SqlSession接口及實(shí)現(xiàn)類(lèi)DefaultSqlSession

          public?interface?SqlSession?{

          ?????List?selectList(String?statementId,?Object...?param)?throws?Exception;

          ?????T?selectOne(String?statementId,?Object...?params)?throws?Exception;

          ????void?close()?throws?SQLException;
          ???
          }
          public?class?DefaultSqlSession?implements?SqlSession?{

          ????private?Configuration?configuration;

          ????//?處理器對(duì)象
          ????private?Executor?simpleExcutor?=?new?SimpleExecutor();

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

          ????@Override
          ????public??List?selectList(String?statementId,?Object...?param)?throws?Exception?{
          ????????//?完成對(duì)?simpleExcutor里的query方法的調(diào)用
          ????????MappedStatement?mappedStatement?=?configuration.getMappedStatementMap().get(statementId);
          ????????List?list?=?simpleExcutor.query(configuration,?mappedStatement,?param);
          ????????return?list;
          ????}

          ????@Override
          ????public??T?selectOne(String?statementId,?Object...?params)?throws?Exception?{
          ????????List?objects?=?selectList(statementId,?params);
          ????????if?(objects.size()?==?1)?{
          ????????????return?(T)?objects.get(0);
          ????????}?else?{
          ????????????throw?new?RuntimeException("返回結(jié)果過(guò)多");
          ????????}
          ????}

          ????@Override
          ????public?void?close()?throws?SQLException?{
          ????????simpleExcutor.close();
          ????}

          }

          5.創(chuàng)建Executor接口及實(shí)現(xiàn)類(lèi)SimpleExecutor實(shí)現(xiàn)類(lèi)

          public?interface?Executor?{

          ?????List?query(Configuration?configuration,?MappedStatement?mappedStatement,?Object...?param)?throws?Exception;

          ????void?close()?throws?SQLException;
          }
          public?class?SimpleExecutor?implements?Executor?{


          ????private?Connection?connection?=?null;

          ????@Override
          ????public??List?query(Configuration?configuration,?MappedStatement?mappedStatement,?Object...?param)?throws?Exception?{
          ????????//?注冊(cè)驅(qū)動(dòng)?獲取連接
          ????????connection?=?configuration.getDataSource().getConnection();

          ????????//?select?*?from?user?where?id?=?#{id}?and?username?=?#{username}
          ????????String?sql?=?mappedStatement.getSql();

          ????????//?對(duì)?sql?進(jìn)行處理
          ????????BoundSql?boundSql?=?getBoundSql(sql);

          ????????//?select?*?from?where?id?=???and?username?=??
          ????????String?finalSql?=?boundSql.getSqlText();

          ????????//?獲取傳入?yún)?shù)類(lèi)對(duì)象
          ????????Class?paramterTypeClass?=?mappedStatement.getParamterType();

          ????????//?獲取預(yù)處理?preparedStatement?對(duì)象
          ????????PreparedStatement?preparedStatement?=?connection.prepareStatement(finalSql);

          ????????//?設(shè)置參數(shù)
          ????????List?parameterMappingList?=?boundSql.getParameterMappingList();
          ????????for?(int?i?=?0;?i?????????????ParameterMapping?parameterMapping?=?parameterMappingList.get(i);
          ????????????String?name?=?parameterMapping.getContent();

          ????????????//?反射??獲取某一個(gè)屬性對(duì)象
          ????????????Field?declaredField?=?paramterTypeClass.getDeclaredField(name);
          ????????????//?設(shè)置暴力訪問(wèn)
          ????????????declaredField.setAccessible(true);

          ????????????//?參數(shù)傳遞的值
          ????????????Object?o?=?declaredField.get(param[0]);
          ????????????//?給占位符賦值
          ????????????preparedStatement.setObject(i?+?1,?o);

          ????????}

          ????????//?執(zhí)行sql
          ????????ResultSet?resultSet?=?preparedStatement.executeQuery();

          ????????//?封裝返回結(jié)果集
          ????????//?獲取返回參數(shù)類(lèi)對(duì)象
          ????????Class?resultTypeClass?=?mappedStatement.getResultType();
          ????????ArrayList?results?=?new?ArrayList<>();
          ????????while?(resultSet.next())?{
          ????????????//?取出?resultSet的元數(shù)據(jù)
          ????????????ResultSetMetaData?metaData?=?resultSet.getMetaData();
          ????????????E?o?=?(E)?resultTypeClass.newInstance();
          ????????????int?columnCount?=?metaData.getColumnCount();
          ????????????for?(int?i?=?1;?i?<=?columnCount;?i++)?{
          ????????????????//?屬性名/字段名
          ????????????????String?columnName?=?metaData.getColumnName(i);
          ????????????????//?屬性值/字段值
          ????????????????Object?value?=?resultSet.getObject(columnName);

          ????????????????//?使用反射或者內(nèi)省?根據(jù)數(shù)據(jù)庫(kù)表和實(shí)體的對(duì)應(yīng)關(guān)系?完成封裝
          ????????????????//?創(chuàng)建屬性描述器?為屬性生成讀寫(xiě)方法
          ????????????????PropertyDescriptor?propertyDescriptor?=?new?PropertyDescriptor(columnName,?resultTypeClass);
          ????????????????//?獲取寫(xiě)方法
          ????????????????Method?writeMethod?=?propertyDescriptor.getWriteMethod();
          ????????????????//?向類(lèi)中寫(xiě)入值
          ????????????????writeMethod.invoke(o,?value);
          ????????????}
          ????????????results.add(o);
          ????????}
          ????????return?results;
          ????}

          ????/**
          ?????*?轉(zhuǎn)換sql語(yǔ)句?完成對(duì)?#{}?的解析工作
          ?????*?1.?將?#{}?使用?進(jìn)行代替
          ?????*?2.?解析出?#{}?里面的值進(jìn)行存儲(chǔ)
          ?????*
          ?????*?@param?sql?轉(zhuǎn)換前的原sql
          ?????*?@return
          ?????*/

          ????private?BoundSql?getBoundSql(String?sql)?{
          ????????//?標(biāo)記處理類(lèi):?主要是配合通用解析器?GenericTokenParser?類(lèi)完成對(duì)配置文件等的解析工作?其中TokenHandler?主要完成處理
          ????????ParameterMappingTokenHandler?parameterMappingTokenHandler?=?new?ParameterMappingTokenHandler();

          ????????//?GenericTokenParser:?通用的標(biāo)記解析器?完成了代碼片段中的占位符的解析?然后根據(jù)給定的標(biāo)記處理器(?TokenHandler?)?來(lái)進(jìn)行表達(dá)式的處理

          ????????//?三個(gè)參數(shù):?分別為?openToken?(開(kāi)始標(biāo)記)、?closeToken?(結(jié)束標(biāo)記)、?handler?(標(biāo)記處理器)
          ????????GenericTokenParser?genericTokenParse?=?new?GenericTokenParser("#{",?"}",?parameterMappingTokenHandler);
          ????????//?解析出來(lái)的sql
          ????????String?parseSql?=?genericTokenParse.parse(sql);
          ????????//?#{}?里面解析出來(lái)的參數(shù)名稱(chēng)
          ????????List?parameterMappings?=?parameterMappingTokenHandler.getParameterMappings();

          ????????BoundSql?boundSql?=?new?BoundSql(parseSql,?parameterMappings);

          ????????return?boundSql;

          ????}

          ????@Override
          ????public?void?close()?throws?SQLException?{
          ????????connection.close();
          ????}
          }
          public?class?BoundSql?{
          ????//?解析過(guò)后的?sql?語(yǔ)句
          ????private?String?sqlText;

          ????//?解析出來(lái)的參數(shù)
          ????private?List?parameterMappingList?=?new?ArrayList<>();
          ????
          ????//?有參構(gòu)造方便創(chuàng)建時(shí)賦值
          ????public?BoundSql(String?sqlText,?List?parameterMappingList)?{
          ????????this.sqlText?=?sqlText;
          ????????this.parameterMappingList?=?parameterMappingList;
          ????}

          ???...?省略getter?setter?方法

          }

          6.測(cè)試代碼

          public?class?IPersistenceTest?{

          ????@Test
          ????public?void?test?()?throws?Exception?{

          ????????InputStream?resourceAsStream?=?Resources.getResourceAsStream("sqlMapConfig.xml");
          ????????SqlSessionFactory?sessionFactory?=?new?SqlSessionFactoryBuilder().build(resourceAsStream);
          ????????SqlSession?sqlSession?=?sessionFactory.openSession();

          ????????User?user?=?new?User();
          ????????user.setId(1);
          ????????user.setUsername("bd2star");
          ????????User?res?=?sqlSession.selectOne("user.selectOne",?user);
          ????????System.out.println(res);
          ????????
          ????????//?關(guān)閉資源
          ???????sqlSession.close()
          ????}
          }

          運(yùn)行結(jié)果如下

          User{id=1,?username='bd2star'}

          測(cè)試通過(guò) 調(diào)整代碼

          創(chuàng)建 接口 Dao及實(shí)現(xiàn)類(lèi)

          public?interface?IUserDao?{

          ????//?查詢所有用戶
          ????public?List?selectList()?throws?Exception;


          ????//?根據(jù)條件進(jìn)行用戶查詢
          ????public?User?selectOne(User?user)?throws?Exception;
          }
          public?class?UserDaoImpl?implements?IUserDao?{
          ????@Override
          ????public?List?findAll()?throws?Exception?{
          ????????InputStream?resourceAsStream?=?Resources.getResourceAsStream("sqlMapConfig.xml");
          ????????SqlSessionFactory?sessionFactory?=?new?SqlSessionFactoryBuilder().build(resourceAsStream);
          ????????SqlSession?sqlSession?=?sessionFactory.openSession();
          ????????List?res?=?sqlSession.selectList("user.selectList");
          ????????sqlSession.close();
          ????????return?res;
          ????}

          ????@Override
          ????public?User?findByCondition(User?user)?throws?Exception?{
          ????????InputStream?resourceAsStream?=?Resources.getResourceAsStream("sqlMapConfig.xml");
          ????????SqlSessionFactory?sessionFactory?=?new?SqlSessionFactoryBuilder().build(resourceAsStream);
          ????????SqlSession?sqlSession?=?sessionFactory.openSession();
          ????????User?res?=?sqlSession.selectOne("user.selectOne",?user);
          ????????sqlSession.close();
          ????????return?res;

          ????}
          }

          調(diào)整測(cè)試方法

          public?class?IPersistenceTest?{

          ????@Test
          ????public?void?test?()?throws?Exception?{
          ????????User?user?=?new?User();
          ????????user.setId(1);
          ????????user.setUsername("bd2star");
          ????????IUserDao?userDao?=?new?UserDaoImpl();
          ????????User?res?=?userDao.findByCondition(user);
          ????????System.out.println(res);
          ????}
          }

          運(yùn)行結(jié)果如下

          User{id=1,?username='bd2star'}

          測(cè)試通過(guò)

          7.補(bǔ)充

          huodd.sql

          --新建數(shù)據(jù)庫(kù)
          CREATE?DATABASE?huodd;
          --使用數(shù)據(jù)庫(kù)
          use?huodd;
          --創(chuàng)建表
          CREATE?TABLE?`user`??(
          ??`id`?int(11)?NOT?NULL,
          ??`username`?varchar(255)?CHARACTER?SET?utf8mb4?COLLATE?utf8mb4_general_ci?NULL?DEFAULT?NULL,
          ??PRIMARY?KEY?(`id`)?USING?BTREE
          )?ENGINE?=?InnoDB?CHARACTER?SET?=?utf8mb4?COLLATE?=?utf8mb4_general_ci?ROW_FORMAT?=?Compact;
          --?插入測(cè)試數(shù)據(jù)
          INSERT?INTO?`user`?VALUES?(1,?'bd2star');
          INSERT?INTO?`user`?VALUES?(2,?'bd3star');

          用到的工具類(lèi)

          GenericTokenParser.java

          public?class?GenericTokenParser?{

          ??private?final?String?openToken;?//開(kāi)始標(biāo)記
          ??private?final?String?closeToken;?//結(jié)束標(biāo)記
          ??private?final?TokenHandler?handler;?//標(biāo)記處理器

          ??public?GenericTokenParser(String?openToken,?String?closeToken,?TokenHandler?handler)?{
          ????this.openToken?=?openToken;
          ????this.closeToken?=?closeToken;
          ????this.handler?=?handler;
          ??}

          ??/**
          ???*?解析${}和#{}
          ???*?@param?text
          ???*?@return
          ???*?該方法主要實(shí)現(xiàn)了配置文件、腳本等片段中占位符的解析、處理工作,并返回最終需要的數(shù)據(jù)。
          ???*?其中,解析工作由該方法完成,處理工作是由處理器handler的handleToken()方法來(lái)實(shí)現(xiàn)
          ???*/

          ??public?String?parse(String?text)?{
          ????//?驗(yàn)證參數(shù)問(wèn)題,如果是null,就返回空字符串。
          ????if?(text?==?null?||?text.isEmpty())?{
          ??????return?"";
          ????}

          ????//?下面繼續(xù)驗(yàn)證是否包含開(kāi)始標(biāo)簽,如果不包含,默認(rèn)不是占位符,直接原樣返回即可,否則繼續(xù)執(zhí)行。
          ????int?start?=?text.indexOf(openToken,?0);
          ????if?(start?==?-1)?{
          ??????return?text;
          ????}

          ???//?把text轉(zhuǎn)成字符數(shù)組src,并且定義默認(rèn)偏移量offset=0、存儲(chǔ)最終需要返回字符串的變量builder,
          ????// text變量中占位符對(duì)應(yīng)的變量名expression。判斷start是否大于-1(即text中是否存在openToken),如果存在就執(zhí)行下面代碼
          ????char[]?src?=?text.toCharArray();
          ????int?offset?=?0;
          ????final?StringBuilder?builder?=?new?StringBuilder();
          ????StringBuilder?expression?=?null;
          ????while?(start?>?-1)?{
          ?????//?判斷如果開(kāi)始標(biāo)記前如果有轉(zhuǎn)義字符,就不作為openToken進(jìn)行處理,否則繼續(xù)處理
          ??????if?(start?>?0?&&?src[start?-?1]?==?'\\')?{
          ????????builder.append(src,?offset,?start?-?offset?-?1).append(openToken);
          ????????offset?=?start?+?openToken.length();
          ??????}?else?{
          ????????//重置expression變量,避免空指針或者老數(shù)據(jù)干擾。
          ????????if?(expression?==?null)?{
          ??????????expression?=?new?StringBuilder();
          ????????}?else?{
          ??????????expression.setLength(0);
          ????????}
          ????????builder.append(src,?offset,?start?-?offset);
          ????????offset?=?start?+?openToken.length();
          ????????int?end?=?text.indexOf(closeToken,?offset);
          ????????while?(end?>?-1)?{////存在結(jié)束標(biāo)記時(shí)
          ??????????if?(end?>?offset?&&?src[end?-?1]?==?'\\')?{//如果結(jié)束標(biāo)記前面有轉(zhuǎn)義字符時(shí)
          ????????????//?this?close?token?is?escaped.?remove?the?backslash?and?continue.
          ????????????expression.append(src,?offset,?end?-?offset?-?1).append(closeToken);
          ????????????offset?=?end?+?closeToken.length();
          ????????????end?=?text.indexOf(closeToken,?offset);
          ??????????}?else?{//不存在轉(zhuǎn)義字符,即需要作為參數(shù)進(jìn)行處理
          ????????????expression.append(src,?offset,?end?-?offset);
          ????????????offset?=?end?+?closeToken.length();
          ????????????break;
          ??????????}
          ????????}
          ????????if?(end?==?-1)?{
          ??????????//?close?token?was?not?found.
          ??????????builder.append(src,?start,?src.length?-?start);
          ??????????offset?=?src.length;
          ????????}?else?{
          ??????????//首先根據(jù)參數(shù)的key(即expression)進(jìn)行參數(shù)處理,返回?作為占位符
          ??????????builder.append(handler.handleToken(expression.toString()));
          ??????????offset?=?end?+?closeToken.length();
          ????????}
          ??????}
          ??????start?=?text.indexOf(openToken,?offset);
          ????}
          ????if?(offset???????builder.append(src,?offset,?src.length?-?offset);
          ????}
          ????return?builder.toString();
          ??}
          }

          ParameterMapping.java

          public?class?ParameterMapping?{

          ????private?String?content;

          ????public?ParameterMapping(String?content)?{
          ????????this.content?=?content;
          ????}

          ????...?省略getter?setter?方法
          }

          ParameterMappingTokenHandler.java

          public?class?ParameterMappingTokenHandler?implements?TokenHandler?{
          ???private?List?parameterMappings?=?new?ArrayList();

          ???//?context是參數(shù)名稱(chēng)?#{id}?#{username}

          ???public?String?handleToken(String?content)?{
          ??????parameterMappings.add(buildParameterMapping(content));
          ??????return?"?";
          ???}

          ???private?ParameterMapping?buildParameterMapping(String?content)?{
          ??????ParameterMapping?parameterMapping?=?new?ParameterMapping(content);
          ??????return?parameterMapping;
          ???}

          ???public?List?getParameterMappings()?{
          ??????return?parameterMappings;
          ???}

          ???public?void?setParameterMappings(List?parameterMappings)?{
          ??????this.parameterMappings?=?parameterMappings;
          ???}

          }

          TokenHandler.java

          public?interface?TokenHandler?{
          ??String?handleToken(String?content);
          }

          繼續(xù)優(yōu)化自定義框架

          通過(guò)上述自定義框架,我們解決了JDBC操作數(shù)據(jù)庫(kù)帶來(lái)的一些問(wèn)題,例如頻繁創(chuàng)建釋放數(shù)據(jù)庫(kù)連接,硬編碼,手動(dòng)封裝返回結(jié)果等問(wèn)題

          但從測(cè)試類(lèi)可以發(fā)現(xiàn)新的問(wèn)題

          • dao 的實(shí)現(xiàn)類(lèi)存在重復(fù)代碼 整個(gè)操作的過(guò)程模板重復(fù) (如創(chuàng)建 SqlSession 調(diào)用 SqlSession方法 關(guān)閉 SqlSession)
          • dao 的實(shí)現(xiàn)類(lèi)中存在硬編碼,如調(diào)用 sqlSession 方法時(shí) 參數(shù) statementId 的硬編碼

          解決方案

          • 通過(guò)代碼模式來(lái)創(chuàng)建接口的代理對(duì)象

          1.添加getMapper方法

          刪除dao的實(shí)現(xiàn)類(lèi) UserDaoImpl.java 我們通過(guò)代碼來(lái)實(shí)現(xiàn)原來(lái)由實(shí)現(xiàn)類(lèi)執(zhí)行的邏輯

          在 SqlSession 中添加 getMapper 方法

          public?interface?SqlSession?{
          ????T?getMapper(Class?mapperClass);
          }

          2. 實(shí)現(xiàn)類(lèi)實(shí)現(xiàn)方法

          DefaultSqlSession 類(lèi)中實(shí)現(xiàn) getMapper 方法

          @Override
          public??T?getMapper(Class?mapperClass)?{
          ????//?使用?JDK?動(dòng)態(tài)代理?來(lái)為?Dao?接口生成代理對(duì)象?并返回
          ????Object?proxyInstance?=?Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(),?new?Class[]{mapperClass},?new?InvocationHandler()?{
          ????????/**
          ?????????*
          ?????????*?@param?proxy?當(dāng)前代理對(duì)象的引用
          ?????????*?@param?method?當(dāng)前被調(diào)用方法的引用
          ?????????*?@param?args?傳遞的參數(shù)
          ?????????*?@return
          ?????????*?@throws?Throwable
          ?????????*/

          ????????@Override
          ????????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{
          ????????????//?底層都還是去執(zhí)行?JDBC?代碼??->?根據(jù)不同情況?調(diào)用?selectList()?或者?selectOne()
          ????????????//?準(zhǔn)備參數(shù)??1.?statmentId?sql語(yǔ)句的唯一標(biāo)識(shí)??namespace.id?=?接口全限定名.方法名
          ????????????//??????????2.?params?->?args
          ????????????
          ????????????//?拿到的是方法名?findAll
          ????????????String?methodName?=?method.getName();
          ????????????//?拿到該類(lèi)的全限定類(lèi)名?com.huodd.dao.IUserDao
          ????????????String?className?=?method.getDeclaringClass().getName();

          ????????????String?statmentId?=?className?+?"."?+?methodName;

          ????????????//?獲取被調(diào)用方法的返回值類(lèi)型
          ????????????Type?genericReturnType?=?method.getGenericReturnType();
          ????????????//?判斷是否進(jìn)行了?泛型類(lèi)型參數(shù)化
          ????????????if?(genericReturnType?instanceof?ParameterizedType)?{
          ????????????????List?list?=?selectList(statmentId,?args);
          ????????????????return?list;
          ????????????}
          ????????????return?selectOne(statmentId,?args);
          ????????}
          ????});

          ????return?(T)?proxyInstance;
          }

          3.調(diào)整mapper.xml配置文件

          這里要注意兩點(diǎn)

          1. namespace 與 dao 接口的全限定類(lèi)名保持一致
          2. id 與 dao 接口中定義的方法名保持一致
          "com.huodd.dao.IUserDao">

          ????
          ????"findAll"?resultType="com.huodd.pojo.User"?paramterType="com.huodd.pojo.User">
          ????????select?*?from?user
          ????

          ????"findByCondition"?paramterType="com.huodd.pojo.User"?resultType="com.huodd.pojo.User">
          ????????select?*?from?user?where?id?=?#{id}?and?username?=#{username}
          ????


          4. 進(jìn)入測(cè)試

          public?class?IPersistenceTest?{

          ????@Test
          ????public?void?test?()?throws?Exception?{

          ????????InputStream?resourceAsStream?=?Resources.getResourceAsStream("sqlMapConfig.xml");
          ????????SqlSessionFactory?sessionFactory?=?new?SqlSessionFactoryBuilder().build(resourceAsStream);
          ????????SqlSession?sqlSession?=?sessionFactory.openSession();
          ????????User?user?=?new?User();
          ????????user.setId(1);
          ????????user.setUsername("bd2star");
          ??//?此時(shí)返回的?userDao?就是代理對(duì)象?所以它的類(lèi)型就是?Proxy
          ????????IUserDao?userDao?=?sqlSession.getMapper(IUserDao.class);
          ????????//?userDao?是代理對(duì)象??調(diào)用了接口中的?findAll()??代理對(duì)象調(diào)用接口中任意方法?都會(huì)執(zhí)行?invoke()
          ????????List?users?=?userDao.findAll();
          ????????System.out.println(users);
          ????????User?res?=?userDao.findByCondition(user);
          ????????System.out.println(res);

          ????}
          }

          運(yùn)行結(jié)果如下

          [User{id=1,?username='bd2star'},?User{id=2,?username='bd3star'}]
          User{id=1,?username='bd2star'}

          目錄結(jié)構(gòu)調(diào)整

          將代碼分為兩個(gè)模塊

          • 提供端(自定義持久層框架-本質(zhì)就是對(duì)JDBC代碼的封裝)
          • 使用端 (引用持久層框架的jar )
            • 包含數(shù)據(jù)庫(kù)配置信息
            • 包含sql配置信息
            • 包含sql語(yǔ)句
            • 參數(shù)類(lèi)型
            • 返回值類(lèi)型

          項(xiàng)目目錄結(jié)構(gòu)最終為

          提供端



          使用端



          源碼地址

          https://gitee.com/bx2star/mybatis-learning.git


          —————END—————


          推薦閱讀:



          最近面試BAT,整理一份面試資料Java面試BAT通關(guān)手冊(cè),覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫(kù)、數(shù)據(jù)結(jié)構(gòu)等等。

          獲取方式:點(diǎn)“在看”,關(guān)注公眾號(hào)并回復(fù)?666?領(lǐng)取,更多內(nèi)容陸續(xù)奉上。

          明天見(jiàn)(??ω??)??
          瀏覽 48
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(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>
                    88国产精品视频一区二区三区 | 国产乱妇无码毛片A片在线看下载 | 成人毛片18女人毛片真水 | 91久久久久久久久久久 | 久久精品99视频 |