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

关于增删改查操作的实现

首先是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);
  }

下面看一下查询的核心方法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();
    }
  }

下面是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);
  }

查询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();
    }
  }

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

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

总结一下
所有的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();
    }
  }

可以发现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();
  }

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

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

  @Override
  public <T> T getMapper(Class<T> type) {
    return configuration.getMapper(type, this);
  }

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

SqlSessionManager

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

接下来的内容

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用中提到,Mybatis是一个可以使用简单的XML或者注解来配置和映射原生信息的框架,它可以将接口和Java的POJO映射成数据库中的记录。同时,Mybatis支持定制化SQL、存储过程以及高级映射,避免了几乎所有的JDBC代码和手动设置参数以及获取结果集的繁琐操作。 要进行Mybatis源码分析,需要深入研究Mybatis的核心组件和原理。其中,SqlSessionFactoryBuilder用于构建SqlSessionFactory,SqlSessionFactory负责创建SqlSessionSqlSession是与数据库交互的主要接口,通过SqlSession可以执行SQL语句并获取结果。在SqlSession的底层,涉及到Executor、StatementHandler、ParameterHandler和ResultSetHandler等核心组件。 Executor负责执行SQL语句,StatementHandler负责处理SQL语句的预编译和参数设置,ParameterHandler负责处理SQL语句的参数传递,ResultSetHandler负责处理SQL语句的结果集。Mybatis通过这些核心组件的协作来完成数据库操作。 在具体操作时,MybatisSQL映射文件(或注解)中定义了SQL语句和参数映射关系,Mybatis会根据配置的Mapper接口和对应的SQL语句,动态生成Mapper接口的实现类。通过动态代理的方式,实现了Mapper接口的方法与SQL语句的绑定,使得开发者可以直接调用Mapper接口的方法来执行SQL语句。 总之,Mybatis源码分析需要深入了解其核心组件和原理,包括SqlSessionFactory、SqlSession、Executor、StatementHandler、ParameterHandler和ResultSetHandler等。通过分析这些组件的工作原理和协作关系,可以更好地理解Mybatis的内部实现机制。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Mybatis源码分析](https://blog.csdn.net/zyyforever/article/details/101289858)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值