深入MyBatis-配置文件-dataSource&Transaction

mybatis数据源dataSource创建

在使用mybatis时,配置数据源是不可缺少的配置项,如下的配置:

  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC" />
      <dataSource type="UNPOOLED">
<!--        <property name="driver" value="com.mysql.jdbc.Driver" />-->
        <property name="driver" value="com.mysql.cj.jdbc.Driver" />
        <property name="url"
                  value="jdbc:mysql://localhost:3306/blogs?useSSL=false" />
        <property name="username" value="root" />
        <property name="password" value="root" />
      </dataSource>
    </environment>
  </environments>
  1. 在mybatis解析到dataSource 标签时,会解析到数据源的类型,上面代码配置的数据源类型是UNPOOLED即没有使用连接池,在mybatis中提供了三种数据源的类型,
类型作用
UnpooledDataSource不用连接池
PooledDataSource简单连接池
JNDIJNDI数据源

创建数据源的代码如下:
XMLConfigBuilder

  private DataSourceFactory dataSourceElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");
      Properties props = context.getChildrenAsProperties();
      DataSourceFactory factory = (DataSourceFactory) resolveClass(type).getDeclaredConstructor().newInstance();
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a DataSourceFactory.");
  }

DataSourceFactory factory = (DataSourceFactory) resolveClass(type).getDeclaredConstructor().newInstance(); 这行代码是创建DataSourceFactory 的代码,我们进行拆解下resolveClass(type)
BaseBuilder:

 protected <T> Class<? extends T> resolveClass(String alias) {
   if (alias == null) {
     return null;
   }
   try {
     return resolveAlias(alias);
   } catch (Exception e) {
     throw new BuilderException("Error resolving class. Cause: " + e, e);
   }
 }
   protected <T> Class<? extends T> resolveAlias(String alias) {
   return typeAliasRegistry.resolveAlias(alias);
 }

TypeAliasRegistry

 public <T> Class<T> resolveAlias(String string) {
    try {
      if (string == null) {
        return null;
      }
      // issue #748
      String key = string.toLowerCase(Locale.ENGLISH);
      Class<T> value;
      if (typeAliases.containsKey(key)) {
        value = (Class<T>) typeAliases.get(key);
      } else {
        value = (Class<T>) Resources.classForName(string);
      }
      return value;
    } catch (ClassNotFoundException e) {
      throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
    }
  }

这个方法最终会调用Configuration的构造方法中初始化的时间指定的typeAliasRegistry中注册的typeAliasRegistry.registerAlias(“JNDI”, JndiDataSourceFactory.class);
typeAliasRegistry.registerAlias(“POOLED”, PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias(“UNPOOLED”, UnpooledDataSourceFactory.class);

Configuration:

public Configuration() {
   typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
   typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

   typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
   typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
   typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
 // 省略其它
 }

resolveClass(type)此时根据配置文件是UNPOOLED,对应的类是UnpooledDataSourceFactory.class,得到这个类的实例后然后调用resolveClass(type).getDeclaredConstructor().newInstance()
进行创建类的实例对像,此时是UnpooledDataSourceFactory的对像,无参构造方法如下:

  public UnpooledDataSourceFactory() {
    this.dataSource = new UnpooledDataSource();
  }

  public UnpooledDataSource() {
  }

在UnpooledDataSource时有一个知识点可以关注下:
UnpooledDataSource

  private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap<>();
  
  static {
    Enumeration<Driver> drivers = DriverManager.getDrivers();
    while (drivers.hasMoreElements()) {
      Driver driver = drivers.nextElement();
      registeredDrivers.put(driver.getClass().getName(), driver);
    }
  }

这个静态代码块中可以把已经注册的数据库驱动遍历出来,然后把实例加入到缓存中,在获取数据库连接时直接从已有的驱动缓存中获取到数据库连接即可,如果驱动不存在registeredDrivers 中是会加载驱动类放入到registeredDrivers 中。

private synchronized void initializeDriver() throws SQLException {
    if (!registeredDrivers.containsKey(driver)) {
      Class<?> driverType;
      try {
        if (driverClassLoader != null) {
          driverType = Class.forName(driver, true, driverClassLoader);
        } else {
          driverType = Resources.classForName(driver);
        }
        // DriverManager requires the driver to be loaded via the system ClassLoader.
        // http://www.kfu.com/~nsayer/Java/dyn-jdbc.html
        Driver driverInstance = (Driver) driverType.getDeclaredConstructor().newInstance();
        DriverManager.registerDriver(new DriverProxy(driverInstance));
        registeredDrivers.put(driver, driverInstance);
      } catch (Exception e) {
        throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
      }
    }
  }

DataSource获取总结:

  1. 解析配置dataSource节点,根据配置数据源type创建相应的数据源创建工厂,mybatis提供的数据源有UnpooledDataSource,PooledDataSource,JNDI。
  2. 把创建好的数据源设置到Environment中。

mybatis TransactionFactory创建

 <transactionManager type="JDBC" />

在配置文件中指定事务类型为JDBC的事务类型,程序解析到JDBC类型时创建。
XMLConfigBuilder.java

  private TransactionFactory transactionManagerElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");
      Properties props = context.getChildrenAsProperties();
      TransactionFactory factory = (TransactionFactory) resolveClass(type).getDeclaredConstructor().newInstance();
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a TransactionFactory.");
  }

JdbcTransactionFactory.java

public class JdbcTransactionFactory implements TransactionFactory {

  @Override
  public Transaction newTransaction(Connection conn) {
    return new JdbcTransaction(conn);
  }

  @Override
  public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
    return new JdbcTransaction(ds, level, autoCommit);
  }
}

JdbcTransaction.java

public class JdbcTransaction implements Transaction {

  private static final Log log = LogFactory.getLog(JdbcTransaction.class);

  protected Connection connection;
  protected DataSource dataSource;
  protected TransactionIsolationLevel level;
  protected boolean autoCommit;

  public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
    dataSource = ds;
    level = desiredLevel;
    autoCommit = desiredAutoCommit;
  }

  public JdbcTransaction(Connection connection) {
    this.connection = connection;
  }

  @Override
  public Connection getConnection() throws SQLException {
    if (connection == null) {
      openConnection();
    }
    return connection;
  }

  @Override
  public void commit() throws SQLException {
    if (connection != null && !connection.getAutoCommit()) {
      if (log.isDebugEnabled()) {
        log.debug("Committing JDBC Connection [" + connection + "]");
      }
      connection.commit();
    }
  }

  @Override
  public void rollback() throws SQLException {
    if (connection != null && !connection.getAutoCommit()) {
      if (log.isDebugEnabled()) {
        log.debug("Rolling back JDBC Connection [" + connection + "]");
      }
      connection.rollback();
    }
  }

  @Override
  public void close() throws SQLException {
    if (connection != null) {
      resetAutoCommit();
      if (log.isDebugEnabled()) {
        log.debug("Closing JDBC Connection [" + connection + "]");
      }
      connection.close();
    }
  }

  protected void setDesiredAutoCommit(boolean desiredAutoCommit) {
    try {
      if (connection.getAutoCommit() != desiredAutoCommit) {
        if (log.isDebugEnabled()) {
          log.debug("Setting autocommit to " + desiredAutoCommit + " on JDBC Connection [" + connection + "]");
        }
        connection.setAutoCommit(desiredAutoCommit);
      }
    } catch (SQLException e) {
      // Only a very poorly implemented driver would fail here,
      // and there's not much we can do about that.
      throw new TransactionException("Error configuring AutoCommit.  "
          + "Your driver may not support getAutoCommit() or setAutoCommit(). "
          + "Requested setting: " + desiredAutoCommit + ".  Cause: " + e, e);
    }
  }

  protected void resetAutoCommit() {
    try {
      if (!connection.getAutoCommit()) {
        // MyBatis does not call commit/rollback on a connection if just selects were performed.
        // Some databases start transactions with select statements
        // and they mandate a commit/rollback before closing the connection.
        // A workaround is setting the autocommit to true before closing the connection.
        // Sybase throws an exception here.
        if (log.isDebugEnabled()) {
          log.debug("Resetting autocommit to true on JDBC Connection [" + connection + "]");
        }
        connection.setAutoCommit(true);
      }
    } catch (SQLException e) {
      if (log.isDebugEnabled()) {
        log.debug("Error resetting autocommit to true "
            + "before closing the connection.  Cause: " + e);
      }
    }
  }

  protected void openConnection() throws SQLException {
    if (log.isDebugEnabled()) {
      log.debug("Opening JDBC Connection");
    }
    connection = dataSource.getConnection();
    if (level != null) {
      connection.setTransactionIsolation(level.getLevel());
    }
    setDesiredAutoCommit(autoCommit);
  }

  @Override
  public Integer getTimeout() throws SQLException {
    return null;
  }

}

总结

在解析配置environments元素时,主要会创建事务和数据源两个主要的对像实例,然后把创建好的实例分别设置到Environment的相应属于中,最终把Environment设置到mybatis配置主类Configuration中,代码主流程如下所示:

private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      if (environment == null) {
        environment = context.getStringAttribute("default");
      }
      for (XNode child : context.getChildren()) {
        String id = child.getStringAttribute("id");
        if (isSpecifiedEnvironment(id)) {
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
          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());
        }
      }
    }
  }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值