概述

上一篇我们讲了configuation.xml中几个标签的解析,例如<properties>,<typeAlises>,<settings>等,今天我们来介绍剩下的两个比较重要的标签之一,<environments>,这个标签主要用于我们访问数据库的配置进行设置。

<environments>解析

我们先来看下configuation.xml中<environments>元素的配置:

  1. 1 <environments default="development">
  2. 2 <environment id="development">
  3. 3 <transactionManager type="JDBC" />
  4. 4 <dataSource type="POOLED">
  5. 5 <property name="driver" value="${driveClass}" />
  6. 6 <property name="url" value="${url}" />
  7. 7 <property name="username" value="${userName}" />
  8. 8 <property name="password" value="${password}" />
  9. 9 </dataSource>
  10. 10 </environment>
  11. 11 </environments>

从上面的具体配置我们可以看出一些东西来:首先我们可以配置多个<environment>标签,它的下面主要配置两个东西,transactionManager和datasource,但是真正起作用的是<environments>标签中default元素标出的那个。下面我们看下具体的解析流程:

  1. 1 private void environmentsElement(XNode context) throws Exception {
  2. 2 if (context != null) {
  3. 3 if (environment == null) {
  4. 4 environment = context.getStringAttribute("default");
  5. 5 }
  6. 6 for (XNode child : context.getChildren()) {
  7. 7 String id = child.getStringAttribute("id");
  8. 8 if (isSpecifiedEnvironment(id)) {
  9. 9 TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
  10. 10 DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
  11. 11 DataSource dataSource = dsFactory.getDataSource();
  12. 12 Environment.Builder environmentBuilder = new Environment.Builder(id)
  13. 13 .transactionFactory(txFactory)
  14. 14 .dataSource(dataSource);
  15. 15 configuration.setEnvironment(environmentBuilder.build());
  16. 16 }
  17. 17 }
  18. 18 }
  19. 19 }

我们从代码中的for 循环遍历可以看出,<environments>标签下面是可以配置多个<environment>子标签的,每个子标签用id来区分,具体使用哪一个,看<environments>的default值,这也印证了我们一开始的分析。第3到5行代码,就是去获取<environments>的default属性,第7行代码,获取子标签<environment>的id属性,判断是不是上面default配置的那个,如果是,第9行代码,根据<transactionManager>标签获取事物管理器:

  1. 1 private TransactionFactory transactionManagerElement(XNode context) throws Exception {
  2. 2 if (context != null) {
  3. 3 String type = context.getStringAttribute("type");
  4. 4 Properties props = context.getChildrenAsProperties();
  5. 5 TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
  6. 6 factory.setProperties(props);
  7. 7 return factory;
  8. 8 }
  9. 9 throw new BuilderException("Environment declaration requires a TransactionFactory.");
  10. 10 }

这边重点看下第5行代码,跟到最后可以发现,它其实是从typeAliasRegistry 这个map中根据type去获取class,进而得到实例,代码如下:

  1. 1 protected Class<?> resolveAlias(String alias) {
  2. 2 return typeAliasRegistry.resolveAlias(alias);
  3. 3 }

那这么的根据获取到是哪个类呢?其实我们上面说到过,其实一些默认的typeAlias,我们再看下关于这边的:

  1. 1 typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
  2. 2 typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
  3. 3
  4. 4 typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
  5. 5 typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
  6. 6 typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

这部分是在configuation中的,类初始化的时候就会加载,我们可以看到,transactionManager有两种配置,JDBC和MANAGED,分别对应于JdbcTransactionFactory和ManagedTransactionFactory。下面我们就来分析下这两个类,首先看下这两个类所在的目录解结构:

我们可以看出这边使用了工厂模式,其中

Transaction是事物接口,主要定义了如下接口:

Connection getConnection() throws SQLException;   // 获取数据库连接

void commit() throws SQLException;  // 提交

void rollback() throws SQLException;  // 回滚

void close() throws SQLException;  // 关闭数据库连接

Integer getTimeout() throws SQLException;  // 获取设置的事物超时时间

TransactionFactory是事物工厂接口,主要定义了如下接口:

void setProperties(Properties props); // 设置属性

Transaction newTransaction(Connection conn); // 根据连接创建事物实例

Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit); // 根据数据源,事物隔离级别,是否自动提交创建事物实例。

这样设计的目的就是为了便于扩展,以后要实现不同类型的事物只要实现这两个接口就行了。

上面配置文件中的JDBC和MANAGED其实就是对应于mybatis内置两种事物类型,JDBCTransaction和ManagedTransaction,

二者的不同之处在于:前者是直接使用JDK提供的JDBC来管理事务的各个环节:提交、回滚、关闭等操作,而后者则什么都不做,那么后者有什么意义呢,当然很重要。当我们单独使用MyBatis来构建项目时,我们要在Configuration配置文件中进行环境(environment)配置,在其中要设置事务类型为JDBC,意思是说MyBatis被单独使用时就需要使用JDBC类型的事务模型,因为在这个模型中定义了事务的各个方面,使用它可以完成事务的各项操作。而MANAGED类型的事务模型其实是一个托管模型,也就是说它自身并不实现任何事务功能,而是托管出去由其他框架来实现,你可能还不明白,这个事务的具体实现就交由如Spring之类的框架来实现,而且在使用SSM整合框架后已经不再需要单独配置环境信息(包括事务配置与数据源配置),因为在在整合jar包(mybatis-spring.jar)中拥有覆盖mybatis里面的这部分逻辑的代码,实际情况是即使你显式设置了相关配置信息,系统也会视而不见......托管的意义显而易见,正是为整合而设。我们学习MyBatis的目的正是由于其灵活性和与Spring等框架的无缝整合的能力,所以有关JDBC事务模块的内容明显不再是MyBatis功能中的重点,也许只有在单独使用MyBatis的少量系统中才会使用到。

但是还是得去了解下,我们先看下JdbcTransactionFactory,这个实现了TransactionFactory,将获取JDBCTransaction实例的过程隐藏了起来,其主要代码如下:

  1. public class JdbcTransactionFactory implements TransactionFactory {
  2.  
  3. @Override
  4. public void setProperties(Properties props) {
  5. }
  6.  
  7. @Override
  8. public Transaction newTransaction(Connection conn) {
  9. return new JdbcTransaction(conn);
  10. }
  11.  
  12. @Override
  13. public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
  14. return new JdbcTransaction(ds, level, autoCommit);
  15. }
  16. }

我们先看setProperties方法,参数是Properties,这个方法是在解析事物标签的时候,如果有属性设置,就要执行,用于覆盖默认的配置,但是对于JdbcTransaction,即使你设置了,也不起作用,因为这边是是一个空方法,当然,我们一般不会设置。

下面两个方法名字一样,只是参数不一样,一个是根据连接去获取JdbcTransaction的实例,一个是根据数据源,事物隔离级别,是否自动提交来获取,讲这两个方法之前,我们先来看下,JdbcTransaction中相关提交、回滚、关闭操作的实现:

提交:

  1. if (connection != null && !connection.getAutoCommit()) {
  2. if (log.isDebugEnabled()) {
  3. log.debug("Committing JDBC Connection [" + connection + "]");
  4. }
  5. connection.commit();
  6. }
  7. }

回滚:

  1. public void rollback() throws SQLException {
  2. if (connection != null && !connection.getAutoCommit()) {
  3. if (log.isDebugEnabled()) {
  4. log.debug("Rolling back JDBC Connection [" + connection + "]");
  5. }
  6. connection.rollback();
  7. }
  8. }

关闭:

  1. public void close() throws SQLException {
  2. if (connection != null) {
  3. resetAutoCommit();
  4. if (log.isDebugEnabled()) {
  5. log.debug("Closing JDBC Connection [" + connection + "]");
  6. }
  7. connection.close();
  8. }
  9. }

我们可以看到,执行这些方法的时候都是针对connection来操作的,首先我们就要获取connection,

获取连接:

  1. 1 public Connection getConnection() throws SQLException {
  2. 2 if (connection == null) {
  3. 3 openConnection();
  4. 4 }
  5. 5 return connection;
  6. 6 }

我们接着看openConnection方法:

  1. protected void openConnection() throws SQLException {
  2. if (log.isDebugEnabled()) {
  3. log.debug("Opening JDBC Connection");
  4. }
  5. connection = dataSource.getConnection();
  6. if (level != null) {
  7. connection.setTransactionIsolation(level.getLevel());
  8. }
  9. setDesiredAutoCommit(autoCommmit);
  10. }

setDesiredAutoCommit的具体内容如下:

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

从这个两个方法我们可以看出,connection可以从datasource中获取,并补充设置它的事物隔离级别,是否自动提交等属性。现在我们再来看一下JdbcTransactionFactory中那两个方法就不难理解了,说到底就是为了获得connection,进而去操作事物。

最后,我们再来看一个JdbcTransaction中的方法,

  1. protected void resetAutoCommit() {
  2. try {
  3. if (!connection.getAutoCommit()) {
  4. // MyBatis does not call commit/rollback on a connection if just selects were performed.
  5. // Some databases start transactions with select statements
  6. // and they mandate a commit/rollback before closing the connection.
  7. // A workaround is setting the autocommit to true before closing the connection.
  8. // Sybase throws an exception here.
  9. if (log.isDebugEnabled()) {
  10. log.debug("Resetting autocommit to true on JDBC Connection [" + connection + "]");
  11. }
  12. connection.setAutoCommit(true);
  13. }
  14. } catch (SQLException e) {
  15. if (log.isDebugEnabled()) {
  16. log.debug("Error resetting autocommit to true "
  17. + "before closing the connection. Cause: " + e);
  18. }
  19. }
  20. }

这个方法是在关闭连接的时候调用的,mybatis默认的自动提交为true,如果将其设置为了false,就需要将其复位。那么自动提交的初始值是在哪边设置的呢?

mybatis源码解析之Configuration加载(二)的更多相关文章

  1. mybatis源码解析之Configuration加载(四)

    概述 上一篇文章,我们主要讲了datasource的相关内容,那么<environments>标签下的内容就看的差不多了,今天就来看一下在拿到transationManager和datas ...

  2. mybatis源码解析之Configuration加载(五)

    概述 前面几篇文章主要看了mybatis配置文件configuation.xml中<setting>,<environments>标签的加载,接下来看一下mapper标签的解析 ...

  3. mybatis源码解析之Configuration加载(三)

    概述 上一篇我们主要分析了下<environments>标签下面,transactionManager的配置,上问最后还有个遗留问题:就是在设置事物管理器的时候有个autocommit的变 ...

  4. mybatis源码解析之Configuration加载(一)

    概要 上一篇,我们主要搭建了一个简单的环境,这边我们主要来分析下mybatis是如何来加载它的配置文件Configuration.xml的. 分析 public class App { public ...

  5. 【MyBatis源码分析】Configuration加载(下篇)

    元素设置 继续MyBatis的Configuration加载源码分析: private void parseConfiguration(XNode root) { try { Properties s ...

  6. 【MyBatis源码分析】Configuration加载(上篇)

    config.xml解析为org.w3c.dom.Document 本文首先来简单看一下MyBatis中将config.xml解析为org.w3c.dom.Document的流程,代码为上文的这部分: ...

  7. webpack4.X源码解析之懒加载

    本文针对Webpack懒加载构建和加载的原理,对构建后的源码进行分析. 一.准备工作 首先,init之后创建一个简单的webpack基本的配置,在src目录下创建两个js文件(一个主入口文件和一个非主 ...

  8. Spring源码解析-配置文件的加载

    spring是一个很有名的java开源框架,作为一名javaer还是有必要了解spring的设计原理和机制,beans.core.context作为spring的三个核心组件.而三个组件中最重要的就是 ...

  9. Mybatis源码学习之资源加载(六)

    类加载器简介 Java虚拟机中的类加载器(ClassLoader)负责加载来自文件系统.网络或其他来源的类文件.Java虚拟机中的类加载器默认使用的是双亲委派模式,如图所示,其中有三种默认使用的类加载 ...

随机推荐

  1. Java的实例化

    实例化是什么 1.在Java中,使用已经定义好的类,创建该类对象的过程称为"实例化". 2.实例化就是在堆中分配内存的过程,分配的内存即对象. 3.只有实例化后的对象,才能访问到类 ...

  2. 在Pycharm中使用Pandas时输出结果中列被省略的解决办法

    在使用pycharm学习pandas的过程中我发现好多时候会发生不能输出所有列的情况,上网搜了一下,发现解决的办法是使用一个输出控制的函数. 在下面的代码中我们只是输出starbucks_store_ ...

  3. SQLAlchemy 增删改查 一对多 多对多

    1.创建数据表 # ORM中的数据表是什么呢? # Object Relation Mapping # Object - Table 通过 Object 去操纵数据表 # 从而引出了我们的第一步创建数 ...

  4. java基础--集合框架的认识

    一.集合框架 对于不知道存储数量和更复杂的方式存储对象用集合框架. 其中有几个常用的接口和实现类:Collection父接口.List接口,Set接口,Map接口, ArrayList实现类.Link ...

  5. druid 连接池加密算法

    package juint; import com.alibaba.druid.filter.config.ConfigTools; public class DruidTest { public s ...

  6. CentOS7.X中使用yum安装nginx的方法

    nginx官方文档说明:http://nginx.org/en/linux_packages.html#RHEL-CentOS 一.安装前准备: yum install yum-utils 二.添加源 ...

  7. linux下查看进程id时用到的命令

    一.查看端口占用的进程 . lsof -i:端口号, 查看某一端口的占用情况 [root@localhost bin]# lsof -i: COMMAND PID USER FD TYPE DEVIC ...

  8. hadoop多文件输出MultipleOutputFormat和MultipleOutputs

    1.MultipleOutputFormat可以将相似的记录输出到相同的数据集.在写每条记录之前,MultipleOutputFormat将调用generateFileNameForKeyValue方 ...

  9. QT 右键弹出菜单

    QWidget及其子类都可有右键菜单 1.设置标志 在widget初始化的时候 setContextMenuPolicy(Qt::CustomContextMenu); 设置为自定义菜单模式 2.在需 ...

  10. xlwt模块的使用

    前记:Python处理表格时会用到xlwt和xlrd模块 xlwt设置行高:row sheet.row(2).set_style(xlwt.easyxf('font:height 440;')) 13 ...