当前位置:   article > 正文

MyBatis源码分析之SqlSession

MyBatis源码分析之SqlSession

SqlSession接口以及门面模式

SqlSession是一个接口类型,它的接口方法包含了如下几个方面的功能

  1. 增删改查的操作
    selectOne,selectList,delete,update,insert等操作
  2. 事务的管理操作
    commit rollback flushstatements close
  3. 获取当前的Connection对象
    getConnection
  4. 获取XNL解析出的配置信息
    getConfiguration
  5. 清空缓存的操作
    clearCache
  6. 实现接口的动态代理
    getMapper

之所以将这么多不同的类型的接口方法定义在一个统一的接口中是为了方便用户使用,通过一个SqlSession’对象就可以完成几乎所有的需求。
当然,该接口的角色是一类似于服务员,这些方法并没有具体实现,而是在用户请求时分派给相应的子系统完成。
这样的设计称为门面模式,SqlSession是门面类。
下面我们看看Mybatis具体实现的SqlSession类,探究门面类下包含了多少个子系统,SqlSession又是如何同它们交互的。

DefaultSqlSession类

我们先看下它的字段以及构造方法

public class DefaultSqlSession implements SqlSession {
  // xml配置对象
  private final Configuration configuration;
  // executor用来执行增删改查操作的对象
  private final Executor executor;
  // 事务管理中的是否自动提交
  private final boolean autoCommit;
  // 用来标记当前会话是否有更新操作,如果有则设为true,表示缓存中可能有脏数据
  private boolean dirty;
  //游标列表
  private List<Cursor<?>> cursorList;
	// 两个构造方法,主要是给私有字段赋值
  public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
  }

  public DefaultSqlSession(Configuration configuration, Executor executor) {
    this(configuration, executor, false);
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

关于增删改查操作的实现

首先是selectOne(查询一条记录)的实现。statement是sql语句的id,通过这个id我们才能知道需要执行哪一条语句,parameter是用于查询的参数,这个参数可以不传,MyBatis实现了方法的重载。
可以发现,selectOne实际上调用的是selectList方法,用来返回查询的对象集合,如果记录数等于一就返回,大于一就抛出异常,否则就返回null。

  @Override
  public <T> T selectOne(String statement, Object parameter) {
    // Popular vote was to return null on 0 results and throw exception on too many.
    List<T> list = this.selectList(statement, parameter);
    if (list.size() == 1) {
      return list.get(0);
    } else if (list.size() > 1) {
      throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
      return null;
    }
  }
 @Override
  public <T> T selectOne(String statement) {
    return this.selectOne(statement, null);
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

下面看一下查询的核心方法selectList。
可以发现selectList可以接受四个参数,并且除了statement不能为空以外,其它三个参数都可以不传,并进行了相应的方法重载。
只需要把重点关注到最后一个selectList的实现。

 @Override
  public <E> List<E> selectList(String statement) {
    return this.selectList(statement, null);
  }

  @Override
  public <E> List<E> selectList(String statement, Object parameter) {
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
  }

  @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    return selectList(statement, parameter, rowBounds, Executor.NO_RESULT_HANDLER);
  }

  private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
  	/* statement表示sql语句的id
	*  parameter是用来查询的参数
	* rowBounds是控制分页的参数
	* handler是用来处理结果集函数接口
	*/ 
    try {
    // 通过id从configuration中查找到了Statement对象,它其中封装了这条sql语句的相关信息,比如具体的Sql语句是什么,缓存等等
      MappedStatement ms = configuration.getMappedStatement(statement);
     // 实际的查询是通过executor的query方法查找的
     //这个wrapCollection是干什么的呢?
     /* 它是用来判断当前传递的参数是不是一个Collection,并会进行相应的处理
     * 现在我们并不需要知道query里的具体逻辑是什么,只需要知道我们查询时实际调用的是executor的query方法
	*/
      return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

下面是select的三种重载形式,它事实上调用的也是selectList方法,区别是它没有返回值,且必须传入ResultHandler类

@Override
  public void select(String statement, Object parameter, ResultHandler handler) {
    select(statement, parameter, RowBounds.DEFAULT, handler);
  }

  @Override
  public void select(String statement, ResultHandler handler) {
    select(statement, null, RowBounds.DEFAULT, handler);
  }

  @Override
  public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
    selectList(statement, parameter, rowBounds, handler);
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

查询Cursor调用的是executor的queryCursor方

 @Override
  public <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      Cursor<T> cursor = executor.queryCursor(ms, wrapCollection(parameter), rowBounds);
      registerCursor(cursor);
      return cursor;
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

update相较于select较为简单,就是query的update方法来更新参数,它只需要sql的id和用来更新的参数
值得注意的是,一但sqlSession执行了update方法,dirty就会被设置为true,用来标记缓存中可能有脏数据

public int update(String statement) {
    return update(statement, null);
  }

  @Override
  public int update(String statement, Object parameter) {
    try {
      dirty = true;
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.update(ms, wrapCollection(parameter));
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

insert和delet本质也是数据的更新,所以调用的也是executor的update方法。

 @Override
  public int insert(String statement) {
    return insert(statement, null);
  }

  @Override
  public int insert(String statement, Object parameter) {
    return update(statement, parameter);
  }
   @Override
  public int delete(String statement) {
    return update(statement, null);
  }

  @Override
  public int delete(String statement, Object parameter) {
    return update(statement, parameter);
  }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

总结一下
所有的select操作依赖于selectList和selectCursor,而它们又依赖于executor的query和queryCursor方法
所有的delet,insert,update操作都依赖于update,而它又依赖于executor的query方法。

关于事务管理的实现

/*
* 该方法判断是否应该提交事务或者回滚事务,分析可知,在不是自动提交事务的情况下,只有dirty为true时才需要回滚或者提交
* dirty只有在执行更新操作以后才会设置为true,并且在commit或者rollback之后重新置为false
* 而commit和rollback同样是调用了executor的相应接口
*/
private boolean isCommitOrRollbackRequired(boolean force) {
    return (!autoCommit && dirty) || force;
  }
@Override
  public void commit() {
    commit(false);
  }

  @Override
  public void commit(boolean force) {
    try {
      executor.commit(isCommitOrRollbackRequired(force));
      dirty = false;
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error committing transaction.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

  @Override
  public void rollback() {
    rollback(false);
  }

  @Override
  public void rollback(boolean force) {
    try {
      executor.rollback(isCommitOrRollbackRequired(force));
      dirty = false;
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error rolling back transaction.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

可以发现getConnection和clearCache同样依赖于executor

@Override
  public Connection getConnection() {
    try {
      return executor.getTransaction().getConnection();
    } catch (SQLException e) {
      throw ExceptionFactory.wrapException("Error getting a new connection.  Cause: " + e, e);
    }
  }

  @Override
  public void clearCache() {
    executor.clearLocalCache();
  }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

下面两个是和Configuration的相关对象

 @Override
  public Configuration getConfiguration() {
    return configuration;
  }

  @Override
  public <T> T getMapper(Class<T> type) {
    return configuration.getMapper(type, this);
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

最后总结分析一下DefaultSqlSession的几个字段
executor 负责增删改查,事务管理,清理缓存和获取连接对象的操作
dirty 判断当前是否可能存在脏数据,对事务管理起作用
autoCommit 是否自动提交事务
configuration xml配置对象,可以通过它完成mapper接口类的动态代理,以及通过sql的id查找相关的MappedStatement对象
cursorList 用来保存游标变量的数组

SqlSessionManager

SqlSessionManager同样实现了SqlSession接口,但一般用不到,它主要的功能是对现有的SqlSession对象做代理,增强它的方法。

接下来的内容

在下一篇博文中,我会分析SqlSession中用到的Executor对象,关注具体的查询逻辑。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/158248
推荐阅读
相关标签
  

闽ICP备14008679号