关于MyBatis sqlSession的一点整理

原文地址:关于MyBatis sqlSession的一点整理

工作中,需要学习一下MyBatis sqlSession的产生过程,翻看了mybatis-spring的源码,阅读了一些mybatis的相关doc,对mybatis sqlSession有了一些认知和理解,这里简单的总结和整理一下。

首先, 通过翻阅源码,我们来整理一下mybatis进行持久化操作时重要的几个类:

  • SqlSessionFactoryBuilder:build方法创建SqlSessionFactory实例。
  • SqlSessionFactory:创建SqlSession实例的工厂。
  • SqlSession:用于执行持久化操作的对象,类似于jdbc中的Connection。
  • SqlSessionTemplate:MyBatis提供的持久层访问模板化的工具,线程安全,可通过构造参数或依赖注入SqlSessionFactory实例。

Hibernate是与MyBatis类似的orm框架,这里与Hibernate进行一下对比,Hibernate中对于connection的管理,是通过以下几个重要的类:

  • SessionFactory:创建Session实例的工厂,类似于MyBatis中的SqlSessionFactory。
  • Session:用来执行持久化操作的对象,类似于jdbc中的Connection。
  • HibernateTemplate:Hibernate提供的持久层访问模板化的工具,线程安全,可通过构造参数或依赖注入SessionFactory实例。

在日常的开发中,我们经常需要这样对MyBatis和Spring进行集成,把sqlSessionFactory交给Spring管理,通常情况下,我们这样配置:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
</bean>

通过上面的配置,Spring将自动创建一个SqlSessionFactory对象,其中使用到了org.mybatis.spring.SqlSessionFactoryBean,其 是MyBatis为Spring提供的用于创建SqlSessionFactory的类,将在Spring应用程序的上下文建议一下可共享的 MyBatis SqlSessionFactory实例,我们可以通过依赖注入将SqlSessionFactory传递给MyBatis的一些接口。

如果通过Spring进行事务的管理,我们需要增加Spring注解的事务管理机制,如下配置:

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>
 
<tx:annotation-driven/>

这样,我们就可以使用Spring @Transactional注解,进行事务的控制,表明所注释的方法应该在一个事务中运行。 Spring将在事务成功完成后提交事务,在事务发生错误时进行异常回滚,而且,Spring会将产生的MyBatis异常转换成适当的 DataAccessExceptions,从而提供具体的异常信息。

下面,我们通过分析SqlSessionUtils中getSession的源码,来详细的了解一下sqlSession的产生过程,源码如下:

public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
 
  notNull(sessionFactory, "No SqlSessionFactory specified");
  notNull(executorType, "No ExecutorType specified");
 
  SqlSessionHolder holder = (SqlSessionHolder) getResource(sessionFactory);
 
  if (holder != null && holder.isSynchronizedWithTransaction()) {
    if (holder.getExecutorType() != executorType) {
      throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction");
    }
 
    holder.requested();
 
    if (logger.isDebugEnabled()) {
      logger.debug("Fetched SqlSession [" + holder.getSqlSession() + "] from current transaction");
    }
 
    return holder.getSqlSession();
  }
 
  if (logger.isDebugEnabled()) {
    logger.debug("Creating a new SqlSession");
  }
 
  SqlSession session = sessionFactory.openSession(executorType);
 
  // Register session holder if synchronization is active (i.e. a Spring TX is active)
  //
  // Note: The DataSource used by the Environment should be synchronized with the
  // transaction either through DataSourceTxMgr or another tx synchronization.
  // Further assume that if an exception is thrown, whatever started the transaction will
  // handle closing / rolling back the Connection associated with the SqlSession.
  if (isSynchronizationActive()) {
    Environment environment = sessionFactory.getConfiguration().getEnvironment();
 
    if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
      if (logger.isDebugEnabled()) {
        logger.debug("Registering transaction synchronization for SqlSession [" + session + "]");
      }
 
      holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
      bindResource(sessionFactory, holder);
      registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
      holder.setSynchronizedWithTransaction(true);
      holder.requested();
    } else {
      if (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;
}

上面的getSession方法,会从Spring的事务管理器中获取一个SqlSession或创建一个新的SqlSession,将试图从当前事务中得到一个SqlSession,然后,如果配置有事务管理器的工厂并且Spring 的事务管理器是活跃的,它将会锁定当前事务的SqlSession,保证同步。主要是通过以下几个步骤进行SqlSession的创建:

  1. 它会首先获取SqlSessionHolder,SqlSessionHolder用于在TransactionSynchronizationManager中保持当前的SqlSession。
  2. 如果holder不为空,并且holder被事务锁定,则可以通过holder.getSqlSession()方法,从当前事务中获取sqlSession,即 Fetched SqlSession from current transaction。
  3. 如果不存在holder或没有被事务锁定,则会创建新的sqlSession,即 Creating a new SqlSession,通过sessionFactory.openSession()方法。
  4. 如果当前线程的事务是活跃的,将会为SqlSession注册事务同步,即 Registering transaction synchronization for SqlSession。
时间: 2025-01-01 16:12:20

关于MyBatis sqlSession的一点整理的相关文章

Spring Transaction + MyBatis SqlSession事务管理机制研究学习

线上的系统中,使用的是Spring+Mybatis+Mysql搭建的框架,由于客户需要,最近一直在对性能提升部分进行考虑,主要是涉及Mysql的一些重要参数的配置学习,以及Spring事务管理机制的学习,因为通过观察服务器日志,发现在这两部分的时候耗时比较严重,特别是进行mysql事务提交的时候,项目源码中使用了Spring的声明式事务,即通过@Transactional注解来控制事务的开启与提交,这两天看了一些关于Spring Transaction事务的一些文章,也debug了源码,总算有点

MyBatis概念性面试题整理汇总

MyBatis概念性面试题整理汇总 MyBatis常见的概念性面试题 一.概念性填空题 1.#{}和$ {}的区别是什么?#{}是_____,${}是_____. 2.四个核心接口是()用于执行CRUD操作.()处理SQL的参数.()处理返回结果集.()用于执行SQL语句. 3.MyBatis中提供了一级缓存和二级缓存,其中()默认存在,不可控制,同一SqlSession范围内的操作共享该缓存,增.删.改后将(). 4.Mybatis的Xml映射文件中,不同的Xml映射文件,id是否可以重复?

《算法心得》一点整理

最近在图书馆看到本神书<算法心得:高效算法的奥秘>,主要讲解计算机算法的,强调编译器优化和计算机体系结构设计的.虽然看的不大懂,但还是给自己增长了见识和知识.少许整理些自己感兴趣的算法,以备后续温故知新. 1. 操作最右边的位元 a. 将字组中值为1且最靠右的位元置0,如果不存在值为1的位元,则全部结果为0(例如 0101 1110 => 0101 1100): x & (x-1) 这个操作可以判断无符号证书是不是2的幂或者0. b. 将字组中值为0且最靠右的位元置1,如果不存在

关于Java IO InputStream 的一点整理!

程序的开发当中一直在用文件的读写,但是对于java当中输入流以及输出流只是会用不理解,一直以来想搞清楚其,但是一直没有执行(悲剧),今天早上抽出半个小时通过JDK API1.6.0中文版帮助逐步的了解下字节输入流读取字节的方法: 下面就说说InputStream当中read().read(byte[]  b).read(byte[] b.int off .int len)的使用以及区别 一.read()方法: public static void inputStreamRead1() { try

力所能及之关于Mybatis SqlSession工具类

小狼最近在做mybatis的CURD,发现一个小狼不能接受的问题,当然,小狼是比较懒的,见不得冗余代码 每一个CURD操作,小狼都得写一遍下面的代码,一口老血吐出来了... SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); InputStream config = Resources .getResourceAsStream("mybatities-config.xml"

mybatis使用的一点小结:session运行模式及批量提交(转)

mybatis的执行器有三种类型: ExecutorType.SIMPLE 这个类型不做特殊的事情,它只为每个语句创建一个PreparedStatement. ExecutorType.REUSE 这种类型将重复使用PreparedStatements. ExecutorType.BATCH 这个类型批量更新,且必要地区别开其中的select 语句,确保动作易于理解. 可以在配置sqlSession时指定相应的类型: [html] view plain copy <bean id="fsa

关于mybatis扩展的一点想法

本文主要目的是为mybatis的 include 标签添加扩展属性 当使用mybatis进行单表查询时 可以方便的使用 <include> 标签将定义好的<sql> 节点内容包含进来,具体示例如下 <sql id="t_agent_Base_Column_List"> ID, WORKER_ID, <span style="font-family: Arial, Helvetica, sans-serif;">WORK

MyBatis常用SQL语句整理笔记

最近使用Mybatis作为持久层框架,自然/动态sql写得也比较多了,最常见的就是在查询语句中使用if标签来动态地改变过滤条件. Mybatis强大特性之一就是它的动态sql,免除了拼接sql带来的各种麻烦. 在项目开发过程中,常见的和不常见的问题都有碰到过,所以在这总结一下. if choose(when,otherwise) trim(where,set) foreach 1.通常用于多条件组合查询 <select id="productId" parameterType=&

关于单链表反转的一点整理

单链表的反转困扰了我好几天了.今天终于一通百通了,特地记录一下,免得以后又忘记了.脑子笨,只能靠这种办法了. 之前网上的一种做法是这样的: 1 public void reversList(){ 2 Node pre = null; 3 Node next = null; 4 while (head != null) { 5 next = head.next; 6 head.next = pre; 7 pre = head; 8 head = next; 9 } 10 head = pre; 1