Mybatis与Spring集成:SqlSessionTemplate[通俗易懂]

Mybatis与Spring集成:SqlSessionTemplate[通俗易懂]Mybatis与Spring集成:SqlSessionTemplateSqlSessionTemplate构造方法publicSqlSessionTemplate(SqlSessionFactorysqlSessionFactory,ExecutorTypeexecutorType,PersistenceExceptionTranslatorexceptionTra…

大家好,又见面了,我是你们的朋友全栈君。

Mybatis与Spring集成:SqlSessionTemplate

SqlSessionTemplate构造方法

  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;
    //使用JDK动态代理,创建SqlSessionFactory的代理类的实例 
    this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
        new Class[] { 
    SqlSession.class },new SqlSessionInterceptor());
  }

核心在SqlSessionInterceptor的invoke方法中

  private class SqlSessionInterceptor implements InvocationHandler { 
   
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
   
        //重要:获取sqlSession,具体操作见下方详解
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try { 
   
        //调用真实SqlSession的操作方法
        Object result = method.invoke(sqlSession, args);
        //判断当前sqlSession是否被Spring托管;未被Spring托管则自动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);
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) { 
   
          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);
        }
      }
    }
  }

SqlSessionUtils.getSqlSession方法

  //从事务管理器中获取sqlSession或创建一个 
  public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { 
   

    notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
    notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
    //根据sqlSessionFactory从当前线程对应的资源map中获取SqlSessionHolder,当sqlSessionFactory创建了sqlSession,就会在事务管理器中添加一对映射:key为sqlSessionFactory,value为SqlSessionHolder,该类保存sqlSession及执行方式 
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
    //返回holder中的sqlSession,见下方
    SqlSession session = sessionHolder(executorType, holder);
    if (session != null) { 
   
      return session;
    }

    if (LOGGER.isDebugEnabled()) { 
   
      LOGGER.debug("Creating a new SqlSession");
    }
    // 如果holder中返回的sqlSession为空,则创建sqlSession,并注册到holder中去
    session = sessionFactory.openSession(executorType);
    registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);

    return session;
  }
  private static SqlSession sessionHolder(ExecutorType executorType, SqlSessionHolder holder) { 
   
    SqlSession session = null;
    if (holder != null && holder.isSynchronizedWithTransaction()) { 
   
      // 在同一个事务中,执行类型不能变化,原因就是同一个事务中同一个sqlSessionFactory创建的sqlSession会被重用 
      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");
      }

      session = holder.getSqlSession();
    }
    return session;
  }

如何将sqlSession注册到holder中去?

  //开启了事务管理时(如springboot常用的的@Transactional),注册session
  //Register session holder if synchronization is active (i.e. a Spring TX is active).
  private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator, SqlSession session) { 
   
    SqlSessionHolder holder;
    ///判断同步是否激活,只要SpringTX被激活,就是true 
    if (TransactionSynchronizationManager.isSynchronizationActive()) { 
   
      //加载环境变量,判断注册的事务管理器是否是Spring管理事务,如果是,则将sqlSession加载进事务管理的本地线程缓存中;否则报错
      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);
        // 以sessionFactory为key,hodler为value,加入到TransactionSynchronizationManager管理的本地缓存ThreadLocal<Map<Object, Object>>中 
        TransactionSynchronizationManager.bindResource(sessionFactory, holder);
        TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
        //设置当前holder和当前事务同步
        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");
      }
    }
}

这里用到了TransactionSynchronizationManager类:

TransactionSynchronizationManager.bindResource:在调用一个需要事务的组件的时候,管理器首先判断当前线程有没有事务,如果没有事务则启动一个事务,并把事务与当前线程绑定。Spring使用TransactionSynchronizationManager的bindResource方法将当前线程与一个事务绑定,采用的方式是ThreadLocal。

    public static void bindResource(Object key, Object value) throws IllegalStateException { 
   
        Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
        Assert.notNull(value, "Value must not be null");
        // 此处resources是一个全局变量: 
        //private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal("Transactional resources");
        Map<Object, Object> map = (Map)resources.get();
        if (map == null) { 
   
            map = new HashMap();
            resources.set(map);
        }
        //判断以此sessionFactory为key的Map是否存在。如果存在则此线程已经绑定了 
        Object oldValue = ((Map)map).put(actualKey, value);
        if (oldValue instanceof ResourceHolder && ((ResourceHolder)oldValue).isVoid()) { 
   
            oldValue = null;
        }

        if (oldValue != null) { 
   
            throw new IllegalStateException("Already value [" + oldValue + "] for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");
        } else { 
   
            if (logger.isTraceEnabled()) { 
   
                logger.trace("Bound value [" + value + "] for key [" + actualKey + "] to thread [" + Thread.currentThread().getName() + "]");
            }

        }
    }

可以看出:多线程获取SqlSession时,先从当前线程中获取SqlSessionHolder中的sqlSession;如果为空,再新建sqlSession。所以,多线程公用的是一个SqlSessionTemplate(默认bean的单例注入),但不共用一个SqlSession。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/138199.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • GPU利用率低的解决办法

    GPU利用率低的解决办法watch-n0.1-dnvidia-smi#检查GPU利用率参数解决办法:1.dataloader设置参数2.增大batchsize3.减少IO操作,比如tensorboard的写入和打印。4.换显卡

    2022年6月30日
    70
  • Python获取int最大值和float最大值

    Python获取int最大值和float最大值计算机所能表示的最大值 根据你的计算机的位数决定 有机计算机是 64 位 有的是 32 位 因此具体情况各不相同 本人的电脑是 64 位的 1 获得 int 型的最大值 importsysMAX INT sys maxsizeprint MAX INT 2 获得 float 型的最大值灰常简单 max float float inf 是的 你没有看错 最大的浮点数就是这个 inf

    2025年6月10日
    0
  • Eclipse创建SRC目录下面创建包出现在SRC目录同级[通俗易懂]

    Eclipse创建SRC目录下面创建包出现在SRC目录同级[通俗易懂]Eclipse创建SRC目录下面创建包出现在SRC目录同级

    2022年4月24日
    75
  • word2vec的原理及实现(附github代码)

    word2vec的原理及实现(附github代码)目录一、word2vec原理二、word2vec代码实现(1)获取文本语料(2)载入数据,训练并保存模型①#输出日志信息②#将语料保存在sentence中③#生成词向量空间模型④#保存模型(3)加载模型,实现功能(4)增量训练最近要用到文本词向量,借此机会重温一下word2vec。本文会讲解word2vec的原理和代码实现。本…

    2022年5月17日
    33
  • 可变与不可变数据类型

    Python中的数据类型以Python3为例,Python3中有以下几个标准的数据类型划分可变与不可变类型的依据可变数据类型不可变数据类型如何确定是可变还是不可变数据类型根据可变数据类型

    2022年3月29日
    36
  • 新手入侵笔记_探灵笔记适合新手的角色

    新手入侵笔记_探灵笔记适合新手的角色【拿shell】 1.直接上传aspasajspcerphpaspxhtrcdx格式的木马,不行就利用IIS6.0解析漏洞”:1.asp;1.jpg/1.asp;.jpg/1.asp;jpg/1.asp;.xls 2.上传图片木马遇到拦截系统,连图片木马都上传不了,记事本打开图片木马在代码最前面加上gif89a,一般就能逃过拦截系统了。 3.上传图片木马把地址复制到…

    2022年9月21日
    0

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号