目录
  • 前言
    • jdbc
    • 传统JDBC弊端
    • 思考
  • 源码分析
    • 获取数据源
  • 总结

    前言

    上文讲的MyBatis部署运行且根据官网运行了一个demo:一步到位部署运行MyBatis3源码<保姆级>

    jdbc

    再贴一个JDBC运行的测试方法,流程为:

    • 加载JDBC驱动;
    • 获取数据库连接;
    • 创建JDBC Statements对象;
    • 设置SQL语句的传入参数;
    • 执行SQL语句并获得查询结果;
    • 对查询结果进行转换处理并将处理结果返回;
    • 释放相关资源(关闭Connection,关闭Statement,关闭ResultSet);
        @Test
        public void jdbcTest(){
            String driver = "com.mysql.cj.jdbc.Driver";
            String url = "jdbc:mysql://localhost:3306/news?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true";
            String user = "root";
            String pwd = "root123456";
            Connection connection=null;
            ResultSet rs=null;
            PreparedStatement stmt=null;
            try {
                Class.forName(driver);
                //获取数据库连接
                connection = DriverManager.getConnection(url,user,pwd);
                String sql = "select * from t_level where name=?";
                //创建Statement对象(每一个Statement为一次数据库执行请求)
                stmt=connection.prepareStatement(sql);
                //设置传入参数
                stmt.setString(1,"zhangsan");
                //执行SQL语句
                rs = stmt.executeQuery(sql);
                ResultSetMetaData metaData =rs.getMetaData();
                //处理查询结果-----此处未做操作
                int columnCount= metaData.getColumnCount();
                System.out.println(columnCount);
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                try{
                    //关闭结果集
                    if(rs!=null){
                        rs.close();
                        rs=null;
                    }
                    //关闭执行
                    if(stmt!=null){
                        stmt.close();
                        stmt=null;
                    }
                    if(connection!=null){
                        connection.close();
                        connection=null;
                    }
                }catch(SQLException e){
                    e.printStackTrace();
                }
            }
        }

    传统JDBC弊端

    • JDBC 底层没有用连接池,操作数据库需要频繁的创建和关闭连接,消耗很大的资源;
    • 原生的 JDBC 代码在 Java 中,一旦需要修改 SQL,Java 需要整体编译,不利于系统维护;
    • 使用 PreparedStatement 预编译的话,对变量进行设置 1、2、3 等数字,这样的序号不利于维护;
    • 返回 result 结果集也需要硬编码。

    思考

    拿JDBC测试用例和上文mybatis的测试用例对比,可以发现哪些些共同点?

        @Test
        public void test() throws IOException {
            InputStream input = Resources.getResourceAsStream("SqlSessionConfig.xml");
            SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(input);
            SqlSession sqlSession = sessionFactory.openSession();
            LevelDao dao = sqlSession.getMapper(LevelDao.class);
            List<Level> all = dao.findAll();
        }
    

    首先他们都要有数据源,这是毋庸置疑的。其次还要有执行sql语句,再有就是执行操作。

    源码分析

    接下来进入到源码分析阶段。

    由于我们是根据官网 Building SqlSessionFactory from XML的方式来测试demo的,接下来我们的解析就按照XML文件配置形式来讲解。

    获取数据源

    数据源4大元素包括:驱动、 url、 用户名、 密码。

    在看代码之前,先看一下我们的配置文件结构。

    MyBatis3源码解析之如何获取数据源详解

    mybatis是什么时候获取到数据源的呢?要从测试方法生成SqlSessionFactory说起。

    通过断点进入到SqlSessionFactoryBuilderbuild方法中,方法体就两行关键代码,首先new了一个XML 配置生成器,接着调用了其parse()生成一个Configuration对象。

      public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
        try {
          XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
          return build(parser.parse());
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
          ErrorContext.instance().reset();
          try {
            reader.close();
          } catch (IOException e) {
          }
        }
      }
      public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
      }
    

    parse方法执行了下面这条语句:

    parseConfiguration(parser.evalNode("/configuration"));
    

    parser.evalNode会生成一个mybatis封装的XNode对象,copy后发现就是我们配置文件中<configuration>标签中的内容。

    MyBatis3源码解析之如何获取数据源详解

    MyBatis3源码解析之如何获取数据源详解

    进入到parseConfiguration方法中,可以看出好多方法的字符串参数都和我们<configuration>标签中的一些标签名称相同。没错,每一步都是去扫描到对应参数的标签内容从而进行一些配置处理。

      private void parseConfiguration(XNode root) {
        try {
          //issue #117 read properties first
          propertiesElement(root.evalNode("properties"));
          Properties settings = settingsAsProperties(root.evalNode("settings"));
          loadCustomVfs(settings);
          loadCustomLogImpl(settings);
          typeAliasesElement(root.evalNode("typeAliases"));
          pluginElement(root.evalNode("plugins"));
          objectFactoryElement(root.evalNode("objectFactory"));
          objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
          reflectorFactoryElement(root.evalNode("reflectorFactory"));
          settingsElement(settings);
          // read it after objectFactory and objectWrapperFactory issue #631
          environmentsElement(root.evalNode("environments"));
          databaseIdProviderElement(root.evalNode("databaseIdProvider"));
          typeHandlerElement(root.evalNode("typeHandlers"));
          mapperElement(root.evalNode("mappers"));
        } catch (Exception e) {
          throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
      }
    

    我们此处不研究其他处内容,直接看environmentsElement方法的内容。root.evalNode("environments")返回的XNode对象的value就是我们的environments标签内容。

    MyBatis3源码解析之如何获取数据源详解

    MyBatis3源码解析之如何获取数据源详解

    进入到environmentsElement方法中,会循环遍历下一级的environment,此处便是解析xml配置多数据源的地方。

      private void environmentsElement(XNode context) throws Exception {
        if (context != null) {
          if (environment == null) {
            environment = context.getStringAttribute("default");
          }
          //xml配置多数据源
          for (XNode child : context.getChildren()) {
            String id = child.getStringAttribute("id");
            if (isSpecifiedEnvironment(id)) {
              TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
              //<dataSource></dataSource>
              DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
              //获得到数据库源
              DataSource dataSource = dsFactory.getDataSource();
              Environment.Builder environmentBuilder = new Environment.Builder(id)
                  .transactionFactory(txFactory)
                  .dataSource(dataSource);
              configuration.setEnvironment(environmentBuilder.build());
            }
          }
        }
      }
    

    dataSourceElement方法会拿到dataSource标签的内容生成一个DataSourceFactory ,并根据我们的配置给其属性赋值。

    通过getDataSource()方法便可以拿到我们的数据源。

    最后调用configuration.setEnvironment给到全局配置中的。

    执行流程图如下:

    MyBatis3源码解析之如何获取数据源详解

    总结

    声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。