天天看點

自定義持久層架構前言一、JDBC問題分析二、解決政策分析二、架構設計三、實作總結

自定義持久層架構

文章目錄

    • 自定義持久層架構
  • 前言
  • 一、JDBC問題分析
  • 二、解決政策分析
  • 二、架構設計
    • 1.使用端
    • 2. 架構端
  • 三、實作
    • 1.使用端
    • 2.架構端
  • 總結

前言

例如:為什麼連接配接資料庫不再使用JDBC,而改用諸如Mybatis的持久層架構,此篇通過自定義持久層架構分析對比了二者的差別,及持久層的實作原理

提示:以下是本篇文章正文内容,下面案例可供參考

一、JDBC問題分析

  1. 資料庫連接配接建立、釋放頻繁,造成系統資源浪費,進而影響系統性能;
  2. sql語句存在寫死,不易擴充和維護;
  3. 查詢結果集的封裝存在寫死,sql變化會導緻解析變化,不易維護;

二、解決政策分析

  1. 連接配接頻繁---->使用連接配接池
  2. sql語句寫死----->使用配置檔案
  3. 封裝結果集寫死 ------>使用反射、内省封裝pojo對象

二、架構設計

1.使用端

提供核心配置檔案

sqlMapConfig.xml 提供資料庫配置資訊,引入mapper.xml

mapper.xml 提供sql語句配置資訊

2. 架構端

  1. 加載配置檔案,以位元組流讀取到記憶體中

    建立Resources類,getResourceAsStaream方法,傳回InputStream

  2. 建立Bean對象,存放解析出來的内容

    Configuration:核心配置類,存放sqlMapConfig.xml 解析出來的内容;

    MappedStatement:映射配置類,存放mapper.xml解析春來的内容;

    Configuration 中引入MappedStatement,便于傳遞執行。

  3. 解析配置檔案

    建立類SqlSessionFactoryBuilder

    建立方法build

    該方法做了兩件事:

    第一,dom4j解析位元組輸入流,封裝成Configuration對象中;

    第二,建立工廠類SqlSessionFactory對象。

  4. 建立SQLSessionFactory接口及實作類DefaultSqlSessionFactory

    方法openSession,該方法擷取SqlSession接口的執行個體類對象

  5. 建立SqlSession接口及實作類DefaultSession

    建立CRUD方法:selectList()、selectOne()、insert()、update()、delete()等,方法中調用Executor接口的實力類對象的方法

    優化:在SqlSession中添加getMapper方法,通過代理實作(代理對象在調用任何方法,都會執行getMapper中的invoke方法)

  6. 建立Executor接口及實作類SimpleExecutor

    該類的crud方法中執行的就是jdbc的代碼:注冊驅動獲連接配接、擷取sql、擷取預處理對象、設定參數、執行sql、封裝結果集

三、實作

1.使用端

sqlMapConfig.xml 代碼如下:

<configuration>
    <!-- 資料庫配置資訊  -->
    <dataSource>
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/lagou?useUnicode=true&amp;characterEncoding=utf8"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
    </dataSource>
    <!--存放mapper.xml全路徑-->
    <mapper resource="UserMapper.xml"></mapper>
</configuration>
           

sqlMapConfig.xml 代碼如下:

<mapper namespace="com.lagou.dao.IUserDao">

<!--    sql唯一辨別:namespace.id組成:statementId-->
    <select id="findAll" resultType="com.lagou.pojo.User">
        select * from user
    </select>

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

    <insert id="addUser" paramterType="com.lagou.pojo.User">
        insert into user values(#{id},#{username})
    </insert>

    <update id="updateUser" paramterType="com.lagou.pojo.User">
        update user set username = #{username} where id = #{id}
    </update>

    <delete id="deleteUser" paramterType="com.lagou.pojo.User">
        delete from user where id = #{id}
    </delete>



</mapper>
           

2.架構端

Resources.java代碼如下:

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

SqlSessionFactoryBuilder.java代碼如下:

public SqlSessionFactory build(InputStream in) throws PropertyVetoException, DocumentException {

        //第一:使用dom4j解析配合檔案,将解析出來的配置檔案封裝到Configuration中
        XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder();
        Configuration configuration = xmlConfigBuilder.parseConfig(in);

        //第二:建立SqlsessionFactory對象:工廠類:生産sqlSession:會話對象
        DefaultSqlSessionFactory defaultSqlSessionFactory = new DefaultSqlSessionFactory(configuration);

        return defaultSqlSessionFactory;
    }
           

XMLConfigBuilder.java代碼如下:

/**
     *
     * 該方法就是将配置檔案進行解析,封裝到Configuration
     */
    public Configuration parseConfig(InputStream in) throws DocumentException, PropertyVetoException {
        Document document = new SAXReader().read(in);
        //根标簽<configuration>
        Element rootElement = document.getRootElement();
        List<Element> list = rootElement.selectNodes("//property");
        Properties properties = new Properties();
        for (Element element : list) {
            String name = element.attributeValue("name");
            String value = element.attributeValue("value");
            properties.setProperty(name,value);
        }
        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<Element> mapperList = rootElement.selectNodes("//mapper");
        for (Element element : mapperList) {
            String mapperPath = element.attributeValue("resource");
            InputStream resourceAsStaream = Resources.getResourceAsStaream(mapperPath);
            XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(configuration);
            xmlMapperBuilder.parse(resourceAsStaream);
        }



        return configuration;
    }
           

xmlMapperBuilder.java代碼如下:

public void parse(InputStream inputStream) throws DocumentException {
        Document document = new SAXReader().read(inputStream);
        Element rootElement = document.getRootElement();
        String namesapce = rootElement.attributeValue("namespace");
        List<Element> selectlist = rootElement.selectNodes("//select");
        List<Element> insertlist = rootElement.selectNodes("//insert");
        List<Element> updatelist = rootElement.selectNodes("//update");
        List<Element> deletelist = rootElement.selectNodes("//delete");
        //組裝方法類型與标簽集合的對應關系
        Map map = new HashMap<String,Object>();
        map.put("select",selectlist);
        map.put("insert",insertlist);
        map.put("update",updatelist);
        map.put("delete",deletelist);

        Iterator<Map.Entry<String, Object>> it = map.entrySet().iterator();
        while(it.hasNext()){
            Map.Entry<String, Object> entry = it.next();
            dealConfiguration(configuration,(List<Element>)entry.getValue(),(String)entry.getKey(),namesapce);
        }

    }


    /**
     把配置檔案增删改查類型的标簽配置到MappedStatement,進而封裝到Configuration
     */
    private void dealConfiguration(Configuration configuration,List<Element> list,String methodType,String namesapce){
        for (Element element : list) {
            String id = element.attributeValue("id");
            String resultType = element.attributeValue("resultType");
            String paramterType = element.attributeValue("paramterType");
            String sqlText = element.getTextTrim();

            MappedStatement mappedStatement = new MappedStatement();
            mappedStatement.setId(id);
            mappedStatement.setResultType(resultType);
            mappedStatement.setParamterType(paramterType);
            mappedStatement.setSql(sqlText);
            mappedStatement.setMethodType(methodType);
            String key = namesapce + "." + id;
            configuration.getMappedStatementMap().put(key,mappedStatement);
        }

    }
           

DefaultSqlSession.java代碼如下:

@Override
    public <E> List<E> selectList(String statementid, Object... params) throws Exception{
        //完成對SimpleExecutor裡query方法的調用
        SimpleExecutor simpleExecutor = new SimpleExecutor();
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementid);
        List<Object> list = simpleExecutor.query(configuration, mappedStatement, params);
        return (List<E>) list;
    }
           

DefaultSqlSession.java代碼如下:

@Override
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception {

        //1.注冊驅動擷取連接配接
        Connection connection = configuration.getDataSource().getConnection();
        //2.擷取SQL語句:  select * from user where id=#{id} and username=#{username}
        String sql = mappedStatement.getSql();
        //解析sql語句: select * from user where id=? and username=? 轉換過程中,對#{}裡面的值進行存儲
        BoundSql boundSql = getBoundSql(sql);

        //3. 擷取預處理對象:paramterStatement
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());

        //4. 設定參數
         //擷取參數全路徑
        String paramterType = mappedStatement.getParamterType();
        Class<?> paramterClass = getClassType(paramterType);
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        for (int i = 0; i < parameterMappings.size(); i++) {
            ParameterMapping parameterMapping = parameterMappings.get(i);
            String content = parameterMapping.getContent();
            //反射
            Field declaredField = paramterClass.getDeclaredField(content);
            //暴力通路
            declaredField.setAccessible(true);
            Object o = declaredField.get(params[0]);
            preparedStatement.setObject(i+1,o);
        }

        //5. 執行sql
        ResultSet resultSet = preparedStatement.executeQuery();
        String resultType = mappedStatement.getResultType();
        Class<?> resultTypeClass = getClassType(resultType);

        ArrayList<Object> objects = new ArrayList<>();

        //6. 封裝傳回結果集
        while(resultSet.next()){
            Object o = resultTypeClass.newInstance();
            //中繼資料,因為中繼資料帶有列名
            ResultSetMetaData metaData = resultSet.getMetaData();
            for (int i = 1; i <= metaData.getColumnCount(); i++) {
                //字段名
                String columnName = metaData.getColumnName(i);
                //字段值
                Object value = resultSet.getObject(columnName);
                //使用反射或内省,根據資料庫表和實體的對應關系完成封裝
                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName,resultTypeClass);
                Method writeMethod = propertyDescriptor.getWriteMethod();
                writeMethod.invoke(o,value);
            }
            objects.add(o);
        }


        return (List<E>) objects;
    }
           

總結

以上就是自定義持久層架構的思路及内容,其中對sql語句解析的類時mybatis中自帶的類。

繼續閱讀