Mybatis源码解析(四) —— SqlSession是如何实现数据库操作的?

Mybatis源码解析(四) —— SqlSession是如何实现数据库操作的?

??如果拿一次数据库请求操作做比喻,那么前面3篇文章就是在做请求准备,真正执行操作的是本篇文章要讲述的内容。正如标题一样,本篇文章最最核心的要点就是 SqlSession实现数据库操作的源码解析。但按照惯例,我这边依然列出如下的问题:

  • 1、 SqlSession 是如何被创建的? 每次的数据库操作都会创建一个新的SqlSession么?(也许有很多同学会说SqlSession是通过 SqlSessionFactory.openSession() 创建,但这个答案按照10分制顶多给5分)
  • 2、 SqlSession与事务(Transaction)之间的关系? 在同一个方法中,Mybatis多次请求数据库,是否要创建多个SqlSession?
  • 3、 SqlSession是如何实现数据库操作的?

?? 本章内容就是围绕着上面三个问题进行解析,那么带着问题去看源码吧!

一、SqlSession 的创建

?? 在学习Mybatis时,我们常常看到的 SqlSession 创建方式是 SqlSessionFactory.openSession() ,那么我们就拿此作为切入点,先来看看 SqlSessionFactory.openSession() 的方法源码(注意 是 DefaultSqlSessionFactory ),其内部是调用 openSessionFromDataSource() 方法,那么我们来下这个方法内部源码:


  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      // 创建了一个 TransactionFactory 事务工厂( 如果有仔细看过 SqlSessionFactoryBean.buildSqlSessionFactory() 过程的同学,应该能够看到是 SpringManagedTransactionFactory )
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      // 通过 TransactionFactory 获取了一个 事务 Transaction
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      // 根据 execType(默认是 SIMPLE ) 获取了一个 Executor (真正执行数据库操作的对象)
      final Executor executor = configuration.newExecutor(tx, execType);
      // 返回了一个 DefaultSqlSession 对象
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
  

?? 整个SqlSession 的创建分 3个步骤:

  • 1、 获取到 TransactionFactory 事务工厂对象 ( 如果有仔细看过 SqlSessionFactoryBean.buildSqlSessionFactory() 过程的同学,应该能够看到是 SpringManagedTransactionFactory )
  • 2、 通过 TransactionFactory 获取了一个 事务 Transaction
  • 3、 根据 execType(默认是 SIMPLE ) 获取了一个Executor (真正执行数据库操作的对象)
  • 4、 创建并返回 DefaultSqlSession 对象

?? 通过源码我们知道每次 SqlSession(准确地说是 DefaultSqlSession )的创建都会 有一个 Transaction(在Mybatis-Spring 中 是 SpringManagedTransaction ) 事务对象 的生成。也就是说:

  • 1、 一个事务 Transaction 对象与一个 SqlSession 对象 是一一对应的关系。
  • 2、 同一个SqlSession 不管执行多少次数据库操作。只要没有执行close,那么整个操作都是在同一个 Transaction 中执行的。

??看过之前的文章的同学应该有疑问,之前不管是创建MapperProxy 的 SqlSession 还是 MapperMethod中调用的SqlSession其实都是 SqlSessionTemplate ,与这里的 DefaultSqlSession 不是同一个SqlSession对象。那么我们就来简单分析下这2者之间的区别与职责吧!

SqlSessionTemplateDefaultSqlSession 之间不可告人的秘密

??在之前的文章中,我们讲到过 每创建一个 MapperFactoryBean 就会创建一个 SqlSessionTemplate 对象,而 MapperFactoryBean 在获取 MapperProxy 时会将 SqlSessionTemplate 传递到 MapperProxy中。 也就是说 SqlSessionTemplate 的生命周期是与 MapperProxy 的生命周期是一致的。( 注意: MapperProxy 是被注入到Spring容器中的,所以结果不难想象)

?? 在之前的文章中,我们简单的描述过 SqlSessionTemplate 内部维护了一个 sqlSessionProxy ,而 sqlSessionProxy 是通过动态代理创建的一个 SqlSession 对象, SqlSessionTemplate 的 数据库操作方法 insert/update 等等都是委托 sqlSessionProxy 来执行的。那么我们想看下这个 sqlSessionProxy 的创建:


  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
    notNull(executorType, "Property 'executorType' is required");

    this.sqlSessionFactory = sqlSessionFactory;
    this.executorType = executorType;
    this.exceptionTranslator = exceptionTranslator;
    // 通过 Proxy.newProxyInstance() 动态代理创建的
    this.sqlSessionProxy = (SqlSession) newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class },
        new SqlSessionInterceptor());
  }
  

?? 在创建 sqlSessionProxy 的代码中,我们可以发现其指定的代理对象是 SqlSessionInterceptor(SqlSession拦截器?),那么关键代码肯定在这个 SqlSessionInterceptor 中,查看 SqlSessionInterceptor发现其是一个内部类,其源码如下:


 private class SqlSessionInterceptor implements InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      // 根据条件获取一个SqlSession(注意此时的SqlSession 是 DefaultSqlSession ),此时的SqlSession 可能是新创建的,也可能是上一次的请求的SqlSession。
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
        // 反射执行 SqlSession 方法
        Object result = method.invoke(sqlSession, args);
        // 判断当前的 SqlSession 是有事务,如果有则不commit
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
          // force commit even on non-dirty sessions because some databases require
          // a commit/rollback before calling close()
          sqlSession.commit(true);
        }
        return result;
      } catch (Throwable t) {
        Throwable unwrapped = unwrapThrowable(t);
        // 判断如果是PersistenceExceptionTranslator且不为空,那么就关闭当前会话,并且将sqlSession置为空防止finally重复关闭
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
          // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
          sqlSession = null;
          Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
          if (translated != null) {
            unwrapped = translated;
          }
        }
        throw unwrapped;
      } finally {
        //只要当前会话不为空, 那么就会关闭当前会话操作,关闭当前会话操作又会根据当前会话是否有事务来决定会话是释放还是直接关闭。
        if (sqlSession != null) {
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
  }
  

?? 整个 invoke 分5个步骤:

  • 1、 根据条件获取一个SqlSession(注意此时的SqlSession 是 DefaultSqlSession ),此时的SqlSession 可能是新创建的,也可能是上一次的请求的SqlSession。
  • 2、 反射执行 SqlSession 方法
  • 3、 判断当前的 SqlSession 是否由事务所管控,如果是则不commit
  • 4、 判断如果是PersistenceExceptionTranslator且不为空,那么就关闭当前会话,并且将sqlSession置为空防止finally重复关闭
  • 5、 只要当前会话不为空, 那么就会关闭当前会话操作,关闭当前会话操作又会根据当前会话是否有事务来决定会话是释放还是直接关闭。

?? 其中步骤1 是本次讨论的核心,其内部流程如下:


 public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sessionFactory, "No SqlSessionFactory specified");
    notNull(executorType, "No ExecutorType specified");

    // 通过 TransactionSynchronizationManager (Spring 的一个事务同步管理器) 获取到一个 SqlSessionHolder (从字面意思就应该明白其内部维护有 SqlSession)
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
    // 判断SqlSessionHolder是否为空、是否与事务同步
    if (holder != null && holder.isSynchronizedWithTransaction()) {
      if (holder.getExecutorType() != executorType) {
        throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction");
      }
      // 将引用计数增加1
      holder.requested();
      // 返回持有的 SqLSession
      return holder.getSqlSession();
    }

    // 如果从 事务同步管理器 没能获取到 一个 SqlSessionHolder 则 调用  sessionFactory.openSession() 新建一个SqlSession
    SqlSession session = sessionFactory.openSession(executorType);

    // 判断当前是否有事务,有则 根据 SqlSession 创建一个 SqlSessionHolder 并将其注册进入到 TransactionSynchronizationManager 中,以供当前事务中的下次使用
    if (TransactionSynchronizationManager.isSynchronizationActive()) {
      Environment environment = sessionFactory.getConfiguration().getEnvironment();

      if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
        holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
        TransactionSynchronizationManager.bindResource(sessionFactory, holder);
        TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
        holder.setSynchronizedWithTransaction(true);
        holder.requested();
      } else {
        if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) {
          if (logger.isDebugEnabled()) {
            logger.debug("SqlSession [" + session + "] was not registered for synchronization because DataSource is not transactional");
          }
        } else {
          throw new TransientDataAccessResourceException(
              "SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
        }
      }
    } else {
      if (logger.isDebugEnabled()) {
        logger.debug("SqlSession [" + session + "] was not registered for synchronization because synchronization is not active");
      }
    }

    return session;
  }

?? 整个流程步骤分一下几步:

  • 1、 通过 TransactionSynchronizationManager (Spring 的一个事务同步管理器) 获取到一个 SqlSessionHolder (从字面意思就应该明白其内部维护有 SqlSession)
  • 2、 判断SqlSessionHolder是否为空、是否与事务同步,是则返回持有的 SqLSession
  • 3、 如果从 事务同步管理器 没能获取到 一个 SqlSessionHolder 则 调用 sessionFactory.openSession() 新建一个SqlSession
  • 4、 判断当前是否有事务,有则 根据 SqlSession 创建一个 SqlSessionHolder 并将其注册进入到 TransactionSynchronizationManager 中,以供当前事务中的下次使用

?? 从上面的步骤中,我们发现整个获取SqlSession都与 事务 有极大的关联关系,并且从上面的流程中,我们能够得到几个关键点信息:

  • 1、 同一事务中 不管调用多少次 mapper里的方法 ,最终都是用得同一个 sqlSession,即 一个事务中使用的是同一个sqlSession。
  • 2、 如果没有开启事务,调用一次mapper里的方法将会新建一个 sqlSession 来执行方法。

?? 如果细心的同学会发现这个 TransactionSynchronizationManager 事务同步管理器 是由Spring 持有的,也就是说这里完美的应证了Mybatis-Spring 中对该子项目的描述:

MyBatis-Spring 会帮助你将 MyBatis 代码无缝地整合到 Spring 中。它将允许 MyBatis 参与到 Spring 的事务管理之中,创建映射器 mapper 和 SqlSession 并注入到 bean中

DEBUG测试发现的问题

作者在进行测试DEBUG时发现,只要是开启了事务,最终都会通过 TransactionSynchronizationManager.getResource(sessionFactory) 获取到 SqlSessionHolder ,即便是重启程序后第一次请求也是一样会获取到,并且作者 在 SqlSessionHolder 和 DefaultSqlSession 的 构造函数中打上断点 也并未走到这里,因此作者实在百思不得其解,猜测 TransactionSynchronizationManager.getResource(sessionFactory) 该方法 内部有通过反射动态的创建了SqlSession,但看源码确实没找到,目前实力不被允许!!

?? 成功获取到 SqlSession(准确的说是 DefaultSqlSession ) 后 通过 method.invoke() 反射调用到具体的 DefaultSqlSession 方法。方法调用完成后,判断当前SqlSession是否被事务所管控,如果是则不commit,最后再调用 closeSqlSession() 方法进行SqlSession “关闭”。这里为什么要打引号呢?原因是该方法内部处理时判断了当前SqlSession是否被事务所管控,是的话仅仅将引用计数器减一,并未真正将SqlSession 关闭(这也是为了下次能够使用同一个SqlSession),如果不被事务管控则执行正在的 session.close() 操作。

?? 至此,我们明白了 一个 SqlSession(DefaultSqlSession) 的创建不仅仅是调用一下 sessionFactory.openSession() 那么简单,这其中关联到了 SqlSessionTemplateDefaultSqlSessionSqlSessionInterceptor 以及 Spring 事务(Transaction)(TransactionSynchronizationManager) 。 所以一定要明白这几个对象之间的联系和作用。

二、SqlSession实现数据库操作

?? 从上面的分析我们知道,任何的Mapper接口方法请求最终都会请求到 DefaultSqlSession ,即 DefaultSqlSession 内部封装了数据库操作,其他 SqlSession 子类最终都得依靠它来操作数据库。那么我们就拿 DefaultSqlSession 内部的 selectList() 方法开始讲述其如何封装了数据库操作。


  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      // 从 configuration 中获取到 指定方法的 MappedStatement (注意:statement 是由 MapperMethod 中的 SqlCommand 的 name 字段传下来的,而name 本身就来源于 MappedStatement 的 id ,所以最终  statement 会是 com.xxx.findUserByName 这种形式)
      MappedStatement ms = configuration.getMappedStatement(statement);
      // 通过 委托 Executor 的 query() 执行真正的数据库操作
      List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
      return result;
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

?? 我们可以发现 DefaultSqlSession 的所有方法 基本上都有以下2个步骤:

  • 1、 从 configuration 中获取到 指定方法的 MappedStatement
  • 2、 通过 委托 Executor 来 执行真正的数据库操作

?? 我们知道 MappedStatement 内部保存了所要执行的方法的 SqlSource (保存有从Mapper.xml中解析出来的Sql片段信息),然后通过 Executor 的 query() 方法来执行数据库操作。我们先来看下 Executor 继承关系图:

?? 我们从继承关系图中可以看到 BaseExecutorCachingExecutor ,它们2个分别代表2中缓存机制, BaseExecutor 内部维护了 名为 localCache (PerpetualCache) 的 对象,该对象就是 一级缓存 的实际控制者, CachingExecutor 在二级缓存时使用,其内部实现时通过委托 BaseExecutor 来实现一级缓存的。关于Mybatis缓存机制的内容,大家可以去看看这篇文章——聊聊MyBatis缓存机制

?? 不管是一级缓存还是二级缓存机制,其最终还是会调用到 BaseExecutor,而Mybatis默认的 BaseExecutor 实现是 SimpleExecutor,所以重点关注这2个类的实现,下面是 BaseExecutor 的 query()源码:

  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    // 从 MappedStatement 中获取到 BoundSql(实际上是通过 调用 MappedStatement 中的 SqlSource  的 getBoundSql() 获取)
    BoundSql boundSql = ms.getBoundSql(parameter);
    // 通过参数 解析出 cacheKey ,这个是一级缓存的key
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }

?? 我们可以看到整个方法内部最核心的一点就是 从 MappedStatement 中获取到 BoundSql(实际上是通过 调用 MappedStatement 中的 SqlSource 的 getBoundSql() 获取) ,最后再调用重载方法 query(),这个重发方法做了一级缓存的操作,这里就不描述了,只要知道最后调到了 SimpleExecutor 的 doQuery() 方法就行,查看 doQuery() 方法源码 :


  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      // 从  MappedStatement 中获取到 Configuration
      Configuration configuration = ms.getConfiguration();
      // 通过 Configuration 的 newStatementHandler() 方法创建了一个 StatementHandler 对象
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      // 调用 prepareStatement() 方法 获取到 Statement 对象 (真正执行静态SQl的接口)
      stmt = prepareStatement(handler, ms.getStatementLog());
      // 调用 StatementHandler.query() 方法执行
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }
  

?? 从这里开始就与JDBC 挂钩了,如果熟悉JDBC的同学应该一眼就知道 Statement 这个对象具体时干嘛的了,按照惯例我们还是先分析下 doQuery() 方法内部执行流程步骤:

  • 1、 从 MappedStatement 中获取到 Configuration
  • 2、 通过 Configuration 的 newStatementHandler() 方法创建了一个 StatementHandler 对象
  • 3、 调用 prepareStatement() 方法 获取到 Statement 对象 (真正执行静态SQl的接口)
  • 4、 调用 StatementHandler.query() 方法执行(其实内部委托 Statement 来执行的)

?? 其中不走2、3、4是重点要解析的。 我们先来看下 步骤 2 ,其中涉及到一个关键对象 StatementHandler ,其继承关系图如下:

?? 这个结构与Executor 的类似:

  • 1、 SimpleStatementHandler ,这个对应的 就是JDBC 中常用到的 Statement 接口,用于简单SQL的处理
  • 2、 PreparedStatementHandler , 这个对应的就是JDBC中的 PreparedStatement,用于预编译SQL的处理
  • 3、 CallableStatementHandler , 这个对应JDBC中 CallableStatement ,用于执行存储过程相关的处理
  • 4、 RoutingStatementHandler,这个接口是以上三个接口的路由,没有实际操作,只是负责上面三个StatementHandler的创建及调用

?? 回过头来再看 configuration.newStatementHandler() ,不用猜测,肯定创建的是 RoutingStatementHandler ,并且其内部 的 delegate 默认是 PreparedStatementHandler (MappedStatement builder方法指定了默认的 statementType = StatementType.PREPARED )。

?? 步骤三主要的作用就是 预编译并 获取到Statement(由于是 PreparedStatementHandler 所以默认获取到的是 PreparedStatement), 其 prepareStatement() 方法源码:


  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    // 创建 Connection 链接,查看源码其是通过 transaction.getConnection() 获取到的。
    Connection connection = getConnection(statementLog);
    // 预编译获取到 PrepareStatement ,即 最终会调用到 connection.prepareStatement() 方法
    stmt = handler.prepare(connection);
    // 设置参数信息,其参数是通过 从 BoundSql 获取
    handler.parameterize(stmt);
    return stmt;
  }
  

?? 整个流程分3个步骤:

  • 1、 创建 Connection 链接,查看源码其是通过 transaction.getConnection() 获取到的。
  • 2、 预编译获取到 PrepareStatement ,即 最终会调用到 connection.prepareStatement() 方法
  • 3、 设置参数信息,其参数是通过 从 BoundSql 获取

?? 步骤4会 调用 StatementHandler.query() 方法,以 PreparedStatementHandler 为例,其源码如下:


  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    // 执行SQL
    ps.execute();
    // 通过 ResultSetHandler 的 handleResultSets() 方法解析返回数据
    return resultSetHandler.<E> handleResultSets(ps);
  }

?? 这个方法内部就2个流程步骤:

  • 1、 执行SQL
  • 2、 通过 ResultSetHandler 的 handleResultSets() 方法解析返回数据

?? 这里的 ResultSetHandler 就是用来处理 JDBC中的 ResultSet (相信用过JDBC 的同学对这个不陌生),关于如果解析返回数据的逻辑这里就不在详细分析了。

?? 我们通过上面的分析不难发现从 SqlSession 委托 Executor 执行数据库开始,整个 Executor 的执行操作其实就是封装了 JDBC 的一个执行操作,如果熟悉JDBC的同学相信一眼就能看出来。

三、个人总结

?? SqlSession事务 的关系:

  • 1、 同一事务中 不管调用多少次 mapper里的方法 ,最终都是用得同一个 sqlSession,即 一个事务中使用的是同一个sqlSession。
  • 2、 如果没有开启事务,调用一次mapper里的方法将会新建一个 sqlSession 来执行方法。

?? SqlSession 的一次执行流程图:

本文由博客一文多发平台 OpenWrite 发布!

原文地址:https://www.cnblogs.com/bug9/p/11910005.html

时间: 2024-10-29 04:49:04

Mybatis源码解析(四) —— SqlSession是如何实现数据库操作的?的相关文章

mybatis源码-解析配置文件(四-1)之配置文件Mapper解析(cache)

相关文章推荐 mybatis 缓存的使用, 看这篇就够了 mybatis源码-解析配置文件(四)之配置文件Mapper解析 1. 简介 本文章主要讲解的是, xxxMapper.xml 文件中, cache 节点的源码. 2. 解析 XMLMapperBuilder.cacheElement() 方法主要负责解析 <cache> private void cacheElement(XNode context) throws Exception { if (context != null) {

mybatis源码-解析配置文件(四)之配置文件Mapper解析

在 mybatis源码-解析配置文件(三)之配置文件Configuration解析 中, 讲解了 Configuration 是如何解析的. 其中, mappers作为configuration节点的一部分配置, 在本文章中, 我们讲解解析mappers节点, 即 xxxMapper.xml 文件的解析. 1 解析入口 在解析 mybatis-config.xml 时, 会进行解析 xxxMapper.xml 的文件. 在图示流程的 XMLConfigBuilder.parse() 函数中, 该

Mybatis源码解析,一步一步从浅入深(六):映射代理类的获取

在文章:Mybatis源码解析,一步一步从浅入深(二):按步骤解析源码中我们提到了两个问题: 1,为什么在以前的代码流程中从来没有addMapper,而这里却有getMapper? 2,UserDao明明是我们定义的一个接口类,根本没有定义实现类,那这个userMapper是什么?是mybatis自动为我们生成的实现类吗? 为了更好的解释着两个问题,我们需要重新认识Configuration这个类. 但是在这之前,你需要了解一个概念(设计模式):JAVA设计模式-动态代理(Proxy)示例及说明

mybatis源码分析(四) mybatis与spring事务管理分析

mybatis源码分析(四) mybatis与spring事务管理分析 一丶从jdbc的角度理解什么是事务 从mysql获取一个连接之后, 默认是自动提交, 即执行完sql之后, 就会提交事务. 这种事务的范围是一条sql语句. 将该连接设置非自动提交, 可以执行多条sql语句, 然后由程序决定是提交事务, 还是回滚事务. 这也是我们常说的事务. Connection connection = dataSource.getConnection(); // connection.setTransa

mybatis源码-解析配置文件(三)之配置文件Configuration解析(超详细, 值得收藏)

1. 简介 1.1 系列内容 本系列文章讲解的是mybatis解析配置文件内部的逻辑, 即 Reader reader = Resources.getResourceAsReader("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); 其背后的逻辑. 1.2 适合对象 了解如何使用 mybatis 来访问数据库.可参看<

Spring 源码解析之ViewResolver源码解析(四)

Spring 源码解析之ViewResolver源码解析(四) 1 ViewResolver类功能解析 1.1 ViewResolver Interface to be implemented by objects that can resolve views by name. View state doesn't change during the running of the application, so implementations are free to cache views. I

Mybatis源码解析(一)(2015年06月11日)

一.简介 先看看Mybatis的源码结构图,Mybatis3.2.7版本包含的包共计19个,其他版本可能会少. 每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的,SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得,而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例.

iOS即时通讯之CocoaAsyncSocket源码解析四

原文 前言: 本文为CocoaAsyncSocket源码系列中第二篇:Read篇,将重点涉及该框架是如何利用缓冲区对数据进行读取.以及各种情况下的数据包处理,其中还包括普通的.和基于TLS的不同读取操作等等.注:由于该框架源码篇幅过大,且有大部分相对抽象的数据操作逻辑,尽管楼主竭力想要简单的去陈述相关内容,但是阅读起来仍会有一定的难度.如果不是诚心想学习IM相关知识,在这里就可以离场了... 注:文中涉及代码比较多,建议大家结合源码一起阅读比较容易能加深理解.这里有楼主标注好注释的源码,有需要的

MyBatis源码解析之数据源(含数据库连接池简析)

一.概述: 常见的数据源组件都实现了javax.sql.DataSource接口: MyBatis不但要能集成第三方的数据源组件,自身也提供了数据源的实现: 一般情况下,数据源的初始化过程参数较多,比较复杂: 二.设计模式: 为什么要使用工厂模式 数据源模块类图 : 三.MyBatis源码数据源实现 数据结构如下: 使用动态代理封装了真正的数据库连接对象,进行了方法增强:使用前检查连接是否有效,关闭时对连接进行回收: 连接池配置文件配置的数据在这里映射. 用于管理PooledConnection