• MyBatis原理分析手写持久层框架



    1 JDBC操作数据库问题分析

    在这里插入图片描述

    JDBC API 允许应用程序访问任何形式的表格数据,特别是存储在关系数据库中的数据

    在这里插入图片描述

    代码示例:

    public static void main(String[] args) { 
          Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
      try {
      // 加载数据库驱动
      Class.forName("com.mysql.jdbc.Driver");
      // 通过驱动管理类获取数据库链接
      connection =
    DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis? characterEncoding=utf-8", "root", "root");
      // 定义sql语句?表示占位符
      String sql = "select * from user where username = ?";
      // 获取预处理statement
      preparedStatement = connection.prepareStatement(sql);
      // 设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值 preparedStatement.setString(1, "tom");
      // 向数据库发出sql执行查询,查询出结果集
      resultSet = preparedStatement.executeQuery();
      // 遍历查询结果集
      while (resultSet.next()) {
        int id = resultSet.getInt("id");
        String username = resultSet.getString("username");
      // 封装User
        user.setId(id);
        user.setUsername(username);
      }
        System.out.println(user);
      }
      } catch (Exception e) {
        e.printStackTrace();
      } finally {
          // 释放资源
            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (preparedStatement != null) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    2 JDBC问题分析和解决思路

    剖开代码,逐个分析:

    (1)加载驱动,获取链接:

    在这里插入图片描述

    • 存在问题1:数据库配置信息存在硬编码问题。

      优化思路:使用配置文件!

    • 存在问题2:频繁创建、释放数据库连接问题。

      优化思路:使用数据连接池!

    (2)定义sql、设置参数、执行查询:

    在这里插入图片描述

    • 存在问题3:SQL语句、设置参数、获取结果集参数均存在硬编码问题 。

      优化思路:使用配置文件!

    (2)遍历查询结果集:

    在这里插入图片描述

    • 存在问题4:手动封装返回结果集,较为繁琐

      优化思路:使用Java反射、内省!

    针对JDBC各个环节中存在的不足,现在,我们整理出对应的优化思路,统一汇总:

    存在问题优化思路
    数据库配置信息存在硬编码问题使用配置文件
    频繁创建、释放数据库连接问题使用数据连接池
    SQL语句、设置参数、获取结果集参数均存在硬编码问题使用配置文件
    手动封装返回结果集,较为繁琐使用Java反射、内省

    3 自定义持久层框架_思路分析

    JDBC是个人作战,凡事亲力亲为,低效而高险,自己加载驱动,自己建连接,自己 …

    而持久层框架好比是多工种协作,分工明确,执行高效,有专门负责解析注册驱动建立连接的,有专门管理数据连接池的,有专门执行sql语句的,有专门做预处理参数的,有专门装配结果集的 …

    优化思路: 框架的作用,就是为了帮助我们减去繁重开发细节与冗余代码,使我们能更加专注于业务应用开发。

    3.1 使用JDBC和使用持久层框架区别

    在这里插入图片描述

    是不是发现,拥有这么一套持久层框架是如此舒适,我们仅仅需要干两件事:

    • 配置数据源(地址/数据名/用户名/密码)
    • 编写SQL与参数准备(SQL语句/参数类型/返回值类型)

    框架,除了思考本身的工程设计,还需要考虑到实际项目端的使用场景,干系方涉及两端:

    • 使用端(实际项目)
    • 持久层框架本身

    以上两步,我们通过一张架构图《 手写持久层框架基本思路 》来梳理清楚:

    在这里插入图片描述

    3.2 核心接口/类重点说明

    分工协作角色定位类名定义
    负责读取配置文件资源辅助类Resources
    负责存储数据库连接信息数据库资源类Configuration
    负责存储SQL映射定义、存储结果集映射定义SQL与结果集资源类MappedStatement
    负责解析配置文件,创建会话工厂SqlSessionFactory会话工厂构建者SqlSessionFactoryBuilder
    负责创建会话SqlSession会话工厂SqlSessionFactory
    指派执行器Executor会话SqlSession
    负责执行SQL (配合指定资源Mapped Statement)执行器Executor

    正常来说项目只对应一套数据库环境,一般对应一个SqlSessionFactory实例对象,我们使用单例模式只创建一个SqlSessionFactory实例。

    如果需要配置多套数据库环境,那需要做一些拓展,例如Mybatis中通过environments等配置就可以支持多套测试/生产数据库环境进行切换。

    3.3 项目使用端

    (1)调用框架API,除了引入自定义持久层框架的jar包

    (2)提供两部分配置信息:1.sqlMapConfig.xml : 数据库配置信息(地址/数据名/用户名/密码),以及mapper.xml的全路径

                                                2.mapper.xml : SQL配置信息,存放SQL语句、参数类型、返回值类型相关信息
    
    • 1

    3.4 自定义框架本身

    1、加载配置文件:根据配置文件的路径,加载配置文件成字节输入流,存储在内存中。

    在这里插入图片描述

    2、 创建两个javaBean(容器对象):存放配置文件解析出来的内容

    在这里插入图片描述

    3、解析配置文件(使用dom4j) ,并创建SqlSession会话对象

    在这里插入图片描述

    4、创建SqlSessionFactory接口以及实现类DefaultSqlSessionFactory

    在这里插入图片描述

    5、创建SqlSession接口以及实现类DefaultSqlSession

    在这里插入图片描述

    6、创建Executor接口以及实现类SimpleExecutor

    在这里插入图片描述

    基本过程我们已经清晰,我们再细化一下类图,更好的助于我们实际编码:

    在这里插入图片描述

    3.5 最终手写的持久层框架结构参考

    在这里插入图片描述

    4 自定义持久层框架_编码

      
            
            UTF-8
            UTF-8
            UTF-8
            11
            11
            11
        
    
      
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在使用端项目中创建配置配置文件

    创建 sqlMapConfig.xml

    <configuration> 
        
        <dataSource>
            <property name="driverClassName" value="com.mysql.jdbc.Driver">property>
            <property name="url" value="jdbc:mysql:///zdy_mybatis?useSSL=false&characterEncoding=UTF-8&serverTimezone=UTC">property>
            <property name="username" value="root">property>
            <property name="password" value="root">property>
        dataSource>
    
    
        
        <mappers>
            <mapper resource="mapper/UserMapper.xml">mapper>
        mappers>
    
    configuration> 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    mapper.xml

    <mapper namespace="User">
        
        
        <select id="selectOne" resultType="com.oldlu.pojo.User" parameterType="com.oldlu.pojo.User">
            select * from user where id = #{id} and username = #{username}
        select>
    
      
      
        <select id="selectList" resultType="com.oldlu.pojo.User">
            select * from user
        select>
    mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    User实体

    public class User {
      //主键标识
      private Integer id;
      //用户名
      private String username;
        
      public Integer getId() { 
            return id;
      }
      public void setId(Integer id) { 
            this.id = id;
      }
      public String getUsername() { 
            return username;
      }
      public void setUsername(String username) { 
            this.username = username;
      }
    
        @Override
      public String toString() {
        return "User{" +
        "id=" + id +
        ", username='" + username + '\'' + '}';
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    再创建一个Maven子工程并且导入需要用到的依赖坐标

      <properties>
            
            <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
            <maven.compiler.encoding>UTF-8maven.compiler.encoding>
            <java.version>11java.version>
            <maven.compiler.source>11maven.compiler.source>
            <maven.compiler.target>11maven.compiler.target>
        properties>
    
        <dependencies>
            
            <dependency>
                <groupId>mysqlgroupId>
                <artifactId>mysql-connector-javaartifactId>
                <version>5.1.6version>
            dependency>
    
            
            <dependency>
                <groupId>junitgroupId>
                <artifactId>junitartifactId>
                <version>4.12version>
                
                <scope>testscope>
            dependency>
    
            
            <dependency>
                <groupId>dom4jgroupId>
                <artifactId>dom4jartifactId>
                <version>1.6.1version>
            dependency>
    
            
            <dependency>
                <groupId>jaxengroupId>
                <artifactId>jaxenartifactId>
                <version>1.1.6version>
            dependency>
    
    
            
            <dependency>
                <groupId>com.alibabagroupId>
                <artifactId>druidartifactId>
                <version>1.1.21version>
            dependency>
    
            
            <dependency>
                <groupId>log4jgroupId>
                <artifactId>log4jartifactId>
                <version>1.2.17version>
            dependency>
           
        dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    Resources

    public class Resources {
    
        /**
         * 根据配置文件的路径,加载成字节输入流,存到内存中
         * @param path
         * @return
         */
        public static InputStream getResourceAsSteam(String path){
            InputStream resourceAsStream = Resources.class.getClassLoader().getResourceAsStream(path);
            return resourceAsStream;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    Configuration

    /**
     * 存放核心配置文件解析的内容
     */
    public class Configuration {
    
        // 数据源对象
        private DataSource dataSource;
    
        // map : key :statementId  value : 封装好的MappedStatement
        private Map<String,MappedStatement> mappedStatementMap = new HashMap<>();
    
        public DataSource getDataSource() {
            return dataSource;
        }
    
        public void setDataSource(DataSource dataSource) {
            this.dataSource = dataSource;
        }
    
        public Map<String, MappedStatement> getMappedStatementMap() {
            return mappedStatementMap;
        }
    
        public void setMappedStatementMap(Map<String, MappedStatement> mappedStatementMap) {
            this.mappedStatementMap = mappedStatementMap;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    MappedStatement

    /**
     *  存放解析映射配置文件的内容
     *     
     */
    public class MappedStatement {
    
        // 1.唯一标识 (statementId namespace.id)
        private String statementId;
        // 2.返回结果类型
        private String resultType;
        // 3.参数类型
        private String parameterType;
        // 4.要执行的sql语句
        private String sql;
    
        // 5.mapper代理:sqlcommandType
        private String sqlcommandType;
    
        public String getSqlcommandType() {
            return sqlcommandType;
        }
    
        public void setSqlcommandType(String sqlcommandType) {
            this.sqlcommandType = sqlcommandType;
        }
    
        public String getStatementId() {
            return statementId;
        }
    
        public void setStatementId(String statementId) {
            this.statementId = statementId;
        }
    
        public String getResultType() {
            return resultType;
        }
    
        public void setResultType(String resultType) {
            this.resultType = resultType;
        }
    
        public String getParameterType() {
            return parameterType;
        }
    
        public void setParameterType(String parameterType) {
            this.parameterType = parameterType;
        }
    
        public String getSql() {
            return sql;
        }
    
        public void setSql(String sql) {
            this.sql = sql;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    SqlSessionFactoryBuilder

    public class SqlSessionFactoryBuilder {
    
        /**
         * 1.解析配置文件,封装Configuration 2.创建SqlSessionFactory工厂对象
         * @return
         */
        public SqlSessionFactory build(InputStream inputStream) throws DocumentException {
            // 1.解析配置文件,封装Configuration
            XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder();
            Configuration configuration = xmlConfigBuilder.parse(inputStream);
    
            SqlSessionFactory defatultSqlSessionFactory = new DefatultSqlSessionFactory(configuration);
            return  defatultSqlSessionFactory;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    XMLConfigerBuilder

    public class XMLConfigBuilder {
        
        private Configuration configuration;
    
        public XMLConfigBuilder() {
            configuration = new Configuration();
        }
    
        /**
         * 使用dom4j解析xml文件,封装configuration对象
         * @param inputStream
         * @return
         */
        public Configuration parse(InputStream inputStream) throws DocumentException {
    
            Document document = new SAXReader().read(inputStream);
            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);
            }
    
            // 创建数据源对象(连接池)
            DruidDataSource druidDataSource = new DruidDataSource();
            druidDataSource.setDriverClassName(properties.getProperty("driverClassName"));
            druidDataSource.setUrl(properties.getProperty("url"));
            druidDataSource.setUsername(properties.getProperty("username"));
            druidDataSource.setPassword(properties.getProperty("password"));
    
            // 创建好的数据源对象封装进configuration中、
            configuration.setDataSource(druidDataSource);
    
    
            // 解析映射配置文件
            // 1.获取映射配置文件的路径  2.解析  3.封装好mappedStatement
            List<Element> mapperList = rootElement.selectNodes("//mapper");
            for (Element element : mapperList) {
                String mapperPath = element.attributeValue("resource");
                InputStream resourceAsSteam = Resources.getResourceAsSteam(mapperPath);
                XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(configuration);
                xmlMapperBuilder.parse(resourceAsSteam);
            }
    
            return configuration;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    XMLMapperBuilder

    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<Element> select = rootElement.selectNodes("select");
            for (Element element : select) { //id的值
                String id = element.attributeValue("id");
                String paramterType = element.attributeValue("paramterType");
                String resultType = element.attributeValue("resultType"); //输入参数class
                Class<?> paramterTypeClass = getClassType(paramterType);
                //返回结果class
                Class<?> resultTypeClass = getClassType(resultType);
                //statementId
                String key = namespace + "." + id;
                //sql语句
                String textTrim = element.getTextTrim();
                //封装 mappedStatement
                MappedStatement mappedStatement = new MappedStatement();
                mappedStatement.setId(id);
                mappedStatement.setParamterType(paramterTypeClass);
                mappedStatement.setResultType(resultTypeClass);
                mappedStatement.setSql(textTrim);
                //填充 configuration
                configuration.getMappedStatementMap().put(key, mappedStatement);private Class<?> getClassType (String paramterType) throws ClassNotFoundException {
                    Class<?> aClass = Class.forName(paramterType);
                    return aClass;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    sqlSessionFactory 接口及D efaultSqlSessionFactory 实现类

    public interface SqlSessionFactory {
    
        /**
         * 生产sqlSession :封装着与数据库交互的方法
         * @return
         */
        public SqlSession openSession();
    
    }
    
    public class DefatultSqlSessionFactory implements SqlSessionFactory {
    
        private Configuration configuration;
    
        public DefatultSqlSessionFactory(Configuration configuration) {
            this.configuration = configuration;
        }
    
        @Override
        public SqlSession openSession() {
    
            // 执行器创建出来
            Executor executor = new SimpleExecutor();
    
            DefaultSqlSession defaultSqlSession = new DefaultSqlSession(configuration,executor);
            return defaultSqlSession;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    sqlSession 接口及 DefaultSqlSession 实现类

    public interface SqlSession {
    
        /**
         * 查询所有的方法 select * from user where username like '%aaa%' and sex = ''
         * 参数1:唯一标识
         * 参数2:入参
         */
        public <E> List<E> selectList(String statementId,Object parameter) throws Exception;
    
    
        /**
         * 查询单个的方法
         */
        public <T> T selectOne(String statementId,Object parameter) throws Exception;
    }
    
    public class DefaultSqlSession implements SqlSession {
    
        private Configuration configuration;
    
        private Executor executor;
    
        public DefaultSqlSession(Configuration configuration, Executor executor) {
            this.configuration = configuration;
            this.executor = executor;
        }
    
        @Override                    // user.selectList      1 tom user
        public <E> List<E> selectList(String statementId, Object params) throws Exception {
    
            MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
            // 将查询操作委派给底层的执行器
            List<E> list = executor.query(configuration,mappedStatement,params);
    
            return list;
        }
    
        @Override
        public <T> T selectOne(String statementId, Object params) throws Exception {
            List<Object> list = this.selectList(statementId, params);
            if(list.size() == 1){
                return (T) list.get(0);
            }else if(list.size() > 1){
                throw new RuntimeException("返回结果过多");
            }else {
                return null;
            }
        }
    }   
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    Executor

    public interface Executor {
    
        <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object params) throws Exception;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    SimpleExecutor

    public class SimpleExecutor implements Executor {
    
        /**
         * 执行JDBC操作
         * @param configuration
         * @param mappedStatement
         * @param params
         * @param 
         * @return
         */
        @Override                                                                               // user product
        public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object params) throws Exception {
    
            // 1. 加载驱动,获取连接
            Connection connection = configuration.getDataSource().getConnection();
    
            // 2. 获取prepareStatement预编译对象
            /*
                 select * from user where id = #{id} and username = #{username}
                 select * from user where id = ? and username = ?
                 占位符替换 :#{}替换成? 注意:#{id}里面的id名称要保存
             */
            String sql = mappedStatement.getSql();
            BoundSql boundSql = getBoundSQL(sql);
            String finaLSql = boundSql.getFinaLSql();
            PreparedStatement preparedStatement = connection.prepareStatement(finaLSql);
    
            // 3.设置参数
             // 问题1: Object param(类型不确定 user/product/map/String)
             // 问题2:该把对象中的哪一个属性赋值给哪一个占位符呢?
            List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
            if(parameterMappings.size() > 0){
            // com.oldlu.pojo.User
            String parameterType = mappedStatement.getParameterType();
            Class<?> parameterTypeClass = Class.forName(parameterType);
    
            for (int i = 0; i < parameterMappings.size(); i++) {
                ParameterMapping parameterMapping = parameterMappings.get(i);
                // id
                String content = parameterMapping.getContent();
                // 反射
                Field declaredField = parameterTypeClass.getDeclaredField(content);
                // 暴力访问
                declaredField.setAccessible(true);
                Object value = declaredField.get(params);
                preparedStatement.setObject(i+1 ,value);
            }
            }
    
            // 4.执行sql,发起查询
            ResultSet resultSet = preparedStatement.executeQuery();
            String resultType = mappedStatement.getResultType();
            Class<?> resultTypeClass = Class.forName(resultType);
    
            ArrayList<E> list = new ArrayList<>();
            // 5.遍历封装
            while (resultSet.next()){
                // 元数据信息中包含了字段名 字段的值
                ResultSetMetaData metaData = resultSet.getMetaData();
                Object obj = resultTypeClass.newInstance();
                for (int i = 1; i <= metaData.getColumnCount() ; i++) {
    
                    // id  username
                    String columnName = metaData.getColumnName(i);
                    Object value = resultSet.getObject(columnName);
                    // 属性描述器
                    PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName,resultTypeClass);
                    Method writeMethod = propertyDescriptor.getWriteMethod();
                    writeMethod.invoke(obj,value);
                }
                list.add((E) obj);
            }
            return list;
        }
    
        /**
         *  1.将sql中#{}替换成? 2.将#{}里面的值保存
         * @param sql
         * @return
         */
        private BoundSql getBoundSQL(String sql) {
            // 标记处理器:配合标记解析器完成标记的解析工作
            ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
    
            // 标记解析器
            GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
            String finalSql = genericTokenParser.parse(sql);
    
            // #{}里面的值的集合
            List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();
    
            BoundSql boundSql = new BoundSql(finalSql, parameterMappings);
    
            return boundSql;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96

    BoundSql

    public class BoundSql {
       //解析过后的sql语句
        private String sqlText;
        //解析出来的参数
        private List<ParameterMapping> parameterMappingList = new ArrayList<ParameterMapping>();
    
        public BoundSql(String sqlText, List<ParameterMapping>
                parameterMappingList) {
            this.sqlText = sqlText;
            this.parameterMappingList = parameterMappingList;
        }
    
        public String getSqlText() {
            return sqlText;
        }
    
        public void setSqlText(String sqlText) {
            this.sqlText = sqlText;
        }
    
        public List<ParameterMapping> getParameterMappingList() {
            return parameterMappingList;
        }
    
        public void setParameterMappingList(List<ParameterMapping> parameterMappingList) {
            this.parameterMappingList = parameterMappingList;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    5 自定义持久层框架优化

    通过上述我们的自定义框架,我们解决了JDBC操作数据库带来的一些问题:例如频繁创建释放数据库连 接,硬编码,手动封装返回结果集等问题,但是现在我们继续来分析刚刚完成的自定义框架代码,有没 有什么问题?

    问题如下:

    • dao的实现类中存在重复的代码,整个操作的过程模板重复(创建sqlsession,调用sqlsession方 法,关闭 sqlsession)
    • dao的实现类中存在硬编码,调用sqlsession的方法时,参数statement的id硬编码

    解决:使用代理模式来创建接口的代理对象

      @Test
        public void test2() throws Exception {
            InputStream resourceAsSteam = Resources.getResourceAsSteam(path: "sqlMapConfig.xml")
            SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsSteam);
            SqlSession sqlSession = build.openSession();
            User user = new User();
            user.setld(l);
            user.setUsername("tom");
            //代理对象
            UserMapper userMapper = sqlSession.getMappper(UserMapper.class);
            User userl = userMapper.selectOne(user);
            System・out.println(userl);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在sqlSession中添加方法

    public interface SqlSession {
       public  T getMappper(Class mapperClass);
    
    • 1
    • 2

    实现类

    package com.oldlu.sqlSession;
    
    import com.oldlu.executor.Executor;
    import com.oldlu.pojo.Configuration;
    import com.oldlu.pojo.MappedStatement;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.util.Collection;
    import java.util.List;
    
    public class DefaultSqlSession implements SqlSession {
    
        private Configuration configuration;
        private Executor executor;
    
        public DefaultSqlSession(Configuration configuration, Executor executor) {
            this.configuration = configuration;
            this.executor = executor;
        }
    
        @Override
        public <E> List<E> selectList(String statementId, Object param) throws Exception {
    
            // 要传递什么参数呢?
            MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
            List<E> list = executor.query(configuration,mappedStatement,param);
            return list;
        }
    
        @Override
        public <T> T selectOne(String statementId, Object param) throws Exception {
            // 调用selectList方法
            List<Object> list = selectList(statementId, param);
            if(list.size() == 1){
                return (T) list.get(0);
            }else if(list.size() > 1){
                throw new RuntimeException("返回结果过多...");
            }
            return null;
        }
    
        /**
         * 生成代理对象
         * @param mapperClass
         * @param 
         * @return
         */
        @Override
        public <T> T getMapper(Class<?> mapperClass) {
    
            // 使用JDK动态代理生成代理对象
            Object proxyInstance = Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
    
                // 参数1:Object o:代理对象的引用,很少用
                // 参数2:Method method :当前被调用的方法对象
                // 参数3:Object[] objects:被调用的方法的参数
                @Override
                public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
    
                    // 具体的逻辑:执行底层的JDBC
                    // 思路:通过调用sqlSession的方法来完成执行
                    // 问题1:如何获取statementId 根据method获取
                    Class<?> declaringClass = method.getDeclaringClass();
                    // 类全路径= namespace的值
                    String className = declaringClass.getName();
                    String methodName = method.getName();
                    String statementId = className + "." + methodName;
                    MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
    
                    // 问题2:该调用增删改查什么方法呢? 优化:sqlCommandType
                    String sqlCommandType = mappedStatement.getSqlCommandType();
                    switch (sqlCommandType){
                        case "select":
                            //查询操作
                            //问题3:调selectOne还是调selectAll呢?
                            Class<?> returnType = method.getReturnType();
                            boolean assignableFrom = Collection.class.isAssignableFrom(returnType);
                            if(assignableFrom){
                                if(mappedStatement.getParameterType() !=null) {
                                  return   selectList(statementId, objects[0]);
                                }
                                return selectList(statementId, null);
                            }
                            return selectOne(statementId,objects[0]);
    
                        case "update":
                            // 更新操作
                            break;
                        case "insert":
                            // 更新操作
                            break;
                        case "delete":
                            // 更新操作
                            break;
                    }
    
    
                    return null;
                }
            });
    
    
            return (T) proxyInstance;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
  • 相关阅读:
    自动化专业做上位机,现在薪资水平如何?
    js将list数据按日期分组
    Python实用技术二:数据分析和可视化(2)
    如何理解BFC
    How to import dgl-cu113 如何导入 dgl-cu113
    《栈和队列》的模拟实现(顺序栈) (链队列)
    ESP8266-Arduino编程实例-BMM150数字地磁传感器驱动
    离子液体1,3-二甲基咪唑四氟硼酸盐 [C1MIm]PF6 cas:243664-15-5(试剂)
    华为数通方向HCIP-DataCom H12-821题库(单选题:241-260)
    JAVAScript模块化设计
  • 原文地址:https://blog.csdn.net/ZGL_cyy/article/details/132775193