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

作者:张国荣南京分荣 时间:2023-12-06 02:23:08 

前言

上文讲的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源码解析之如何获取数据源详解

来源:https://juejin.cn/post/7113036519350206472

标签:mybatis3,数据源,源码
0
投稿

猜你喜欢

  • Mybatis批量插入Oracle数据的方法实例

    2021-05-24 23:32:31
  • 深入理解Java显式锁的相关知识

    2022-06-16 09:59:41
  • ViewPager打造轮播图Banner/引导页Guide

    2022-06-01 13:56:51
  • Java操作MongoDB数据库的示例代码

    2023-11-23 04:15:51
  • c#使用win32api实现获取光标位置

    2022-05-09 10:59:33
  • android开发之listView组件用法实例简析

    2023-02-05 10:31:52
  • android中RecycleView添加下滑到底部的监听示例

    2022-06-21 17:02:08
  • java基础之Object类

    2022-11-17 21:20:10
  • java抛出异常的几种情况小结

    2022-01-11 05:46:40
  • SpringBoot定制三种错误页面及错误数据方法示例

    2022-03-10 01:15:55
  • SpringBoot深入分析讲解监听器模式上

    2022-06-25 21:04:04
  • SpringBoot 集成 Druid过程解析

    2023-02-25 12:07:59
  • Android开发之时间日期操作实例

    2021-10-20 16:16:55
  • Java实现多线程断点下载实例代码(下载过程中可以暂停)

    2021-06-09 18:02:41
  • 轻松学习C#的结构和类

    2023-12-10 13:46:19
  • Spring JPA find分页示例详解

    2023-05-09 00:36:46
  • Java枚举类用法实例

    2023-09-25 01:47:34
  • SpringBoot借助spring.factories文件跨模块实例化Bean

    2021-12-01 18:22:41
  • 迅速掌握Java容器中常用的ArrayList类与Vector类用法

    2022-12-16 16:18:47
  • mybatis-plus排除非表中字段的操作

    2022-04-22 03:48:41
  • asp之家 软件编程 m.aspxhome.com