最经典的使用代码示例:

String resource = "mybatis-config.xml";
//将XML配置文件构建为Configuration配置类
Reader reader = Resources.getResourceAsReader(resource);
// 通过加载配置文件流构建一个SqlSessionFactory   解析xml文件  1
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);

通过SqlSessionFactoryBuilder()来对配置文件进行各种解析及初始化。

1.build(reader)内部使用new XMLConfigBuilder()工具类解析xml格式文件

new XPathParser(reader)具体解析器,把xml解析成内部Document对象提供后续分解使用,解析整体流程:

2.XMLConfigBuilder()构造方法:

关键点:new Configuration()声明一个configuration对象,默认了很多组件
XMLConfigBuilder初始化调用父类构造方法,

  1. XMLConfigBuilder的父类构造方法默认组件,Configuration是Mybatis的全局配置类,包含Mapper注册工厂等。

public BaseBuilder(Configuration configuration) {
    this.configuration = configuration;
    //POJO默认别名
    this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
    //参数/返回值类型处理映射
    this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
  }
  1. Configuration类是重点配置对象

包含MyBatis所有配置信息,在构造方法默认很多别名组件,使xml在配置时,使用JDBC就直接使用JdbcTransactionFactory.class

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);

    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    typeAliasRegistry.registerAlias("LRU", LruCache.class);
    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

    typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);

    typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
    typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);

    typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
    typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

    typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
    typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    languageRegistry.register(RawLanguageDriver.class);
  }

Configuration在初始化时,默认了很多内部属性。

public class Configuration {

  protected Environment environment;

  protected boolean safeRowBoundsEnabled;
  protected boolean safeResultHandlerEnabled = true;
  protected boolean mapUnderscoreToCamelCase;
  protected boolean aggressiveLazyLoading;
  protected boolean multipleResultSetsEnabled = true;
  protected boolean useGeneratedKeys;
  protected boolean useColumnLabel = true;
  protected boolean cacheEnabled = true;
  protected boolean callSettersOnNulls;
  protected boolean useActualParamName = true;
  protected boolean returnInstanceForEmptyRow;

  protected String logPrefix;
  protected Class<? extends Log> logImpl;
  protected Class<? extends VFS> vfsImpl;
  protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
  protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
  protected Set<String> lazyLoadTriggerMethods = new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString"));
  protected Integer defaultStatementTimeout;
  protected Integer defaultFetchSize;
  protected ResultSetType defaultResultSetType;
  protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
  protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
  protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;

  protected Properties variables = new Properties();
  protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
  protected ObjectFactory objectFactory = new DefaultObjectFactory();
  protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();

  protected boolean lazyLoadingEnabled = false;
  protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL

  protected String databaseId;
  /**
   * Configuration factory class.
   * Used to create Configuration for loading deserialized unread properties.
   *
   * @see <a href='https://code.google.com/p/mybatis/issues/detail?id=300'>Issue 300 (google code)</a>
   */
  protected Class<?> configurationFactory;
  //
  protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
  protected final InterceptorChain interceptorChain = new InterceptorChain();
  protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
  // 类型别名注册器 TypeAliasRegistry构造函数会注册一些, Configuration构造函数会注册一些
  protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
  protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();

  protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
      .conflictMessageProducer((savedValue, targetValue) ->
          ". please check " + savedValue.getResource() + " and " + targetValue.getResource());
  protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
  protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
  protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>("Parameter Maps collection");
  protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<>("Key Generators collection");

  protected final Set<String> loadedResources = new HashSet<>();
  protected final Map<String, XNode> sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers");

  protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<>();
  protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<>();
  protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<>();
  protected final Collection<MethodResolver> incompleteMethods = new LinkedList<>();

  /*
   * A map holds cache-ref relationship. The key is the namespace that
   * references a cache bound to another namespace and the value is the
   * namespace which the actual cache is bound to.
   */
  protected final Map<String, String> cacheRefMap = new HashMap<>();

  public Configuration(Environment environment) {
    this();
    this.environment = environment;
  }

  //...省略
}

其中MapperRegistry属性在注册Mapper时就会解析接口方法上是否使用了注解,如@Select @Insert @Update @Delete @SelectProvider @InsertProvider @UpdateProvider @DeleteProvider

  //Mapper注册器工具类
  protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
  protected final InterceptorChain interceptorChain = new InterceptorChain();
  //入参回参类型解析处理器
  protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
  // 类型别名注册器 TypeAliasRegistry构造函数会注册一些, Configuration构造函数会注册一些
  protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();

3.XMLConfigBUilder.parse()开始解析配置

1.解析的整体流程

最重要的解析xml方法,声明XMLConfigBuilder(reader, environment, properties)并传入xml的Reader对象后,调用XMLConfigBuilder.parse()方法解析xml内容,跟节点是/configuration,看文档 mybatis解析xml配置文件

/**
   * 方法实现说明:解析我们mybatis-config.xml的 configuration节点
   * @author:xsls
   * @param root:configuration节点对象
   * @return:
   * @exception:
   * @date:2019/8/30 15:57
   */
  private void parseConfiguration(XNode root) {
    try {
      /**
       * 解析 properties节点
       *     <properties resource="mybatis/db.properties" />
       *     解析到org.apache.ibatis.parsing.XPathParser#variables
       *           org.apache.ibatis.session.Configuration#variables
       */
      propertiesElement(root.evalNode("properties"));
      /**
       * 解析我们的mybatis-config.xml中的settings节点
       * 具体可以配置哪些属性:http://www.mybatis.org/mybatis-3/zh/configuration.html#settings
       * <settings>
            <setting name="cacheEnabled" value="true"/>
            <setting name="lazyLoadingEnabled" value="true"/>
           <setting name="mapUnderscoreToCamelCase" value="false"/>
           <setting name="localCacheScope" value="SESSION"/>
           <setting name="jdbcTypeForNull" value="OTHER"/>
            ..............
           </settings>
       *
       */
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      /**
       * 基本没有用过该属性
       * VFS含义是虚拟文件系统;主要是通过程序能够方便读取本地文件系统、FTP文件系统等系统中的文件资源。
         Mybatis中提供了VFS这个配置,主要是通过该配置可以加载自定义的虚拟文件系统应用程序
         解析到:org.apache.ibatis.session.Configuration#vfsImpl
       */
      loadCustomVfs(settings);
      /**
       * 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。
       * SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING
       * 解析到org.apache.ibatis.session.Configuration#logImpl
       */
      loadCustomLogImpl(settings);
      /**
       * 解析我们的别名
       * <typeAliases>
           <typeAlias alias="Author" type="cn.liutx.pojo.Author"/>
        </typeAliases>
       <typeAliases>
          <package name="cn.liutx.pojo"/>
       </typeAliases>
       解析到oorg.apache.ibatis.session.Configuration#typeAliasRegistry.typeAliases
       除了自定义的,还有内置的
       */
      typeAliasesElement(root.evalNode("typeAliases"));
      /**
       * 解析我们的插件(比如分页插件)
       * mybatis自带的
       * Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
         ParameterHandler (getParameterObject, setParameters)
         ResultSetHandler (handleResultSets, handleOutputParameters)
         StatementHandler (prepare, parameterize, batch, update, query)
        解析到:org.apache.ibatis.session.Configuration#interceptorChain.interceptors
       */
      pluginElement(root.evalNode("plugins"));

      /**
       * 可以配置  一般不会去设置
       * 对象工厂 用于反射实例化对象、对象包装工厂、
       * 反射工厂 用于属性和setter/getter 获取
       */
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));

      // 设置settings 和默认值到configuration
      settingsElement(settings);

      /**
       * 解析我们的mybatis环境
         <environments default="dev">
           <environment id="dev">
             <transactionManager type="JDBC"/>
             <dataSource type="POOLED">
             <property name="driver" value="${jdbc.driver}"/>
             <property name="url" value="${jdbc.url}"/>
             <property name="username" value="root"/>
             <property name="password" value="Zw726515"/>
             </dataSource>
           </environment>

         <environment id="test">
           <transactionManager type="JDBC"/>
           <dataSource type="POOLED">
           <property name="driver" value="${jdbc.driver}"/>
           <property name="url" value="${jdbc.url}"/>
           <property name="username" value="root"/>
           <property name="password" value="123456"/>
           </dataSource>
         </environment>
       </environments>
       *  解析到:org.apache.ibatis.session.Configuration#environment
       *  在集成spring情况下由 spring-mybatis提供数据源 和事务工厂
       */
      environmentsElement(root.evalNode("environments"));
      /**
       * 解析数据库厂商
       *     <databaseIdProvider type="DB_VENDOR">
                <property name="SQL Server" value="sqlserver"/>
                <property name="DB2" value="db2"/>
                <property name="Oracle" value="oracle" />
                <property name="MySql" value="mysql" />
             </databaseIdProvider>
       *  解析到:org.apache.ibatis.session.Configuration#databaseId
       */
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      /**
       * 解析我们的类型处理器节点
       * <typeHandlers>
            <typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
          </typeHandlers>
          解析到:org.apache.ibatis.session.Configuration#typeHandlerRegistry.typeHandlerMap
       */
      typeHandlerElement(root.evalNode("typeHandlers"));
      /**
       * 解析我们的mapper
       *
       resource:来注册我们的class类路径下的
       url:来指定我们磁盘下的或者网络资源的
       class:
       若注册Mapper不带xml文件的,这里可以直接注册
       若注册的Mapper带xml文件的,需要把xml文件和mapper文件同名 同路径
       -->
       <mappers>
          <mapper resource="mybatis/mapper/EmployeeMapper.xml"/>
          <mapper class="com.liutx.mapper.DeptMapper"></mapper>


            <package name="com.liutx.mapper"></package>
          -->
       </mappers>
       * package
       *     ·解析mapper接口代理工厂(传入需要代理的接口) 解析到:org.apache.ibatis.session.Configuration#mapperRegistry.knownMappers
             ·解析mapper.xml  最终解析成MappedStatement 到:org.apache.ibatis.session.Configuration#mappedStatements
       */
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

2.mapperElement(Node)解析xml文件,判断各种解析方式
四种解析方式,

  • 配置package扫描
    <package name="com.liutx.mapper"></package>

  • 判断从classpath下读取我们的mapper
    <mapper resource="mybatis/mapper/EmployeeMapper.xml"/>

  • 判断是不是从我们的网络资源读取(或者本地磁盘得)
    <mapper url="D:/mapper/EmployeeMapper.xml"/>

  • 解析这种类型(要求接口和xml在同一个包下)
    <mapper class="com.liutx.mapper.DeptMapper"></m
    apper>

/**
   * 方法实现说明
   * @author:xsls
   * @param parent:mappers
   * @return:
   * @exception:
   * @date:2019/8/30 16:32
   */
  private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      /**
       * 获取我们mappers节点下的一个一个的mapper节点
       */
      for (XNode child : parent.getChildren()) {
        /**
         * 判断我们mapper是不是通过批量注册的
         * <package name="com.liutx.mapper"></package>
         */
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        } else {
          /**
           * 判断从classpath下读取我们的mapper
           * <mapper resource="mybatis/mapper/EmployeeMapper.xml"/>
           */
          String resource = child.getStringAttribute("resource");
          /**
           * 判断是不是从我们的网络资源读取(或者本地磁盘得)
           * <mapper url="D:/mapper/EmployeeMapper.xml"/>
           */
          String url = child.getStringAttribute("url");
          /**
           * 解析这种类型(要求接口和xml在同一个包下)
           * <mapper class="com.liutx.mapper.DeptMapper"></mapper>
           *
           */
          String mapperClass = child.getStringAttribute("class");

          /**
           * 我们得mappers节点只配置了
           * <mapper resource="mybatis/mapper/EmployeeMapper.xml"/>
           */
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
            /**
             * 把我们的文件读取出一个流
             */
            InputStream inputStream = Resources.getResourceAsStream(resource);
            /**
             * 创建读取XmlMapper构建器对象,用于来解析我们的mapper.xml文件
             */
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            /**
             * 真正的解析我们的mapper.xml配置文件(说白了就是来解析我们的sql)
             */
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {
            Class<?> mapperInterface = Resources.classForName(mapperClass);
            configuration.addMapper(mapperInterface);
          } else {
            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
          }
        }
      }
    }
  }

3.使用XMLMapperBuilder.parse()对mapper.xml进行解析

/**
   * 方法实现说明:真正的去解析我们的Mapper.xml(EmployeeMapper.xml)
   * @author:xsls
   * @return:
   * @exception:
   * @date:2019/8/30 16:43
   */
  public void parse() {
    /**
     * 判断当前的Mapper是否被加载过
     */
    if (!configuration.isResourceLoaded(resource)) {
      /**
       * 真正的解析我们的 <mapper namespace="com.liutx.mapper.EmployeeMapper">
       *
       */
      configurationElement(parser.evalNode("/mapper"));
      /**
       * 把资源保存到我们Configuration中
       */
        configuration.addLoadedResource(resource);

      bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
  }

扫描包的方式进行解析,先扫描出interface全限定名,在使用MapperAnnotationBuilder.parse()进行解析,解析注解也使用XMLMapperBuilder解析mapper.xml

解析包路径:

/**
  * 判断我们mapper是不是通过批量注册的
  * <package name="com.tuling.mapper"></package>
  */
if ("package".equals(child.getName())) {
   String mapperPackage = child.getStringAttribute("name");
    configuration.addMappers(mapperPackage);
}

具体的解析方法在addMapper()中。

MyBatis源码阅读5.png

4.Mybatis的事务实现

默认使用的是JdbcTransactionFactory. 在与Spring结合后使用的是SpringManagedTransactionFactory


在new Configuration()时提供了默认的类型的别名:

  • typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);

  • typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

在xml加载初始化时解析environments环境变量就会读取jdbc配置和事物配置.
sqlSessionFactory.openSqlsession()时会获取事物配置,new出一个事物

与Spring结合

集成Spring的时候,spring扫描并实例化SqlSessionFactoryBean会调用getObject方法,在afterPropertiesSet()方法中buildSqlSessionFactory()初始化各项配置, 其中数据库事物使用SpringManagedTransactionFactory(),内部使用事务同步器获取数据库链接

/**
 * 为我们的configuration设置一个环境变量
 */
targetConfiguration.setEnvironment(new Environment(this.environment,
  this.transactionFactory == null ? new SpringManagedTransactionFactory(): this.transactionFactory,
  this.dataSource)
);

MyBatis源码阅读6.png默认JdbcTranscation

Mybatis自己实现JdbcTransactionFactory,从dataSources获取数据库链接

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;
  }

}

触发的点在DefaultSqlSessionFactory.openSession()中,执行sql之前会先获取并打开数据库连接,执行的有openSessionFromDataSource()方法,内部判断并获取事务工厂。

Transaction tx = null;
/**
  * 获取事务工厂
  */
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);

以前我们在Mybatis-config.xml中经常看到此JDBC的配置:


<environments default="dev">
  <environment id="dev">
     <transactionManager type="JDBC"/>
     <dataSource type="POOLED">
       <property name="driver" value="${jdbc.driver}"/>
       <property name="url" value="${jdbc.url}"/>
       <property name="username" value="root"/>
       <property name="password" value="Zw726515"/>
     </dataSource>
  </environment>
</environments>

由容器实现

ManagedTransactionFactory是Mybatis不实现事物,由容器来管理事物,内部只有开启和关闭数据库连接,但是没有commit和roback操作。

public class ManagedTransaction implements Transaction {

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

  private DataSource dataSource;
  private TransactionIsolationLevel level;
  private Connection connection;
  private final boolean closeConnection;

  public ManagedTransaction(Connection connection, boolean closeConnection) {
    this.connection = connection;
    this.closeConnection = closeConnection;
  }

  public ManagedTransaction(DataSource ds, TransactionIsolationLevel level, boolean closeConnection) {
    this.dataSource = ds;
    this.level = level;
    this.closeConnection = closeConnection;
  }

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

  @Override
  public void commit() throws SQLException {
    // Does nothing
  }

  @Override
  public void rollback() throws SQLException {
    // Does nothing
  }

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

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

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

}

//下一次了解一下Mbatis的两种注解方式,第一种MapperScan扫描包,第二种在接口上使用@Mapper注解。

文章作者: 刘同学
本文链接:
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 刘同学的小站
中间件 Mybatis
喜欢就支持一下吧