zoukankan      html  css  js  c++  java
  • MyBatist庖丁解牛(五)

    很多时候我们在自己的每个service中没有中注入SqlSessionTemplate;

    但是我们直接调用mapper接口方法就直接能够操作数据库 这个是为什么??下面开始解惑:

    Mybatis SqlSessionTemplate 源码解析

    在使用Mybatis与Spring集成的时候我们用到了SqlSessionTemplate 这个类。

        <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
              <constructor-arg index="0" ref="sqlSessionFactory" />
        </bean>

    通过源码我们何以看到 SqlSessionTemplate 实现了SqlSession接口,也就是说我们可以使用SqlSessionTemplate 来代理以往的DefailtSqlSession完成对数据库的操作,

    但是DefailtSqlSession这个类不是线程安全的,所以这个类不可以被设置成单例模式的。

    如果是常规开发模式 我们每次在使用DefailtSqlSession的时候都从SqlSessionFactory当中获取一个就可以了。

    但是与Spring集成以后,Spring提供了一个全局唯一的SqlSessionTemplate示例 来完成DefailtSqlSession的功能,

    问题就是:无论是多个dao使用一个SqlSessionTemplate,还是一个dao使用一个SqlSessionTemplate,SqlSessionTemplate都是对应一个sqlSession,

    当多个web线程调用同一个dao时,它们使用的是同一个SqlSessionTemplate,也就是同一个SqlSession,那么它是如何确保线程安全的呢?让我们一起来分析一下。

    (1)首先,通过如下代码创建代理类,表示创建SqlSessionFactory的代理类的实例,该代理类实现SqlSession接口,定义了方法拦截器,如果调用代理类实例中实现SqlSession接口定义的方法,该调用则被导向SqlSessionInterceptor的invoke方法

    public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
            Assert.notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
            Assert.notNull(executorType, "Property 'executorType' is required");
            this.sqlSessionFactory = sqlSessionFactory;
            this.executorType = executorType;
            this.exceptionTranslator = exceptionTranslator;
            this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionTemplate.SqlSessionInterceptor());
        }

    核心代码就在 SqlSessionInterceptor的invoke方法当中。

    private class SqlSessionInterceptor implements InvocationHandler {
            private SqlSessionInterceptor() {
            }
    
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
    
                Object unwrapped;
                try {
                    Object result = method.invoke(sqlSession, args);
                    if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
                        sqlSession.commit(true);
                    }
    
                    unwrapped = result;
                } catch (Throwable var11) {
                    unwrapped = ExceptionUtil.unwrapThrowable(var11);
                    if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
                        SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                        sqlSession = null;
                        Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped);
                        if (translated != null) {
                            unwrapped = translated;
                        }
                    }
    
                    throw (Throwable)unwrapped;
                } finally {
                    if (sqlSession != null) {
                        SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                    }
    
                }
    
                return unwrapped;
            }
        }

    在上面的invoke方法当中使用了俩个工具方法 分别是

    SqlSessionUtils.getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator)

    SqlSessionUtils.closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory)

    那么这个俩个方法又是如何与Spring的事物进行关联的呢?

     public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
            Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
            Assert.notNull(executorType, "No ExecutorType specified");
            SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
            SqlSession session = sessionHolder(executorType, holder);
            if (session != null) {
                return session;
            } else {
                LOGGER.debug(() -> {
                    return "Creating a new SqlSession";
                });
                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()) { if (holder.getExecutorType() != executorType) { throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction"); } holder.requested(); LOGGER.debug(() -> { return "Fetched SqlSession [" + holder.getSqlSession() + "] from current transaction"; }); session = holder.getSqlSession(); } return session; }
    复制代码
     1 public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {     
     2     //根据sqlSessionFactory从当前线程对应的资源map中获取SqlSessionHolder,当sqlSessionFactory创建了sqlSession,就会在事务管理器中添加一对映射:key为sqlSessionFactory,value为SqlSessionHolder,该类保存sqlSession及执行方式 
     3     SqlSessionHolder holder = (SqlSessionHolder) getResource(sessionFactory); 
     4  //如果holder不为空,且和当前事务同步 
     5     if (holder != null && holder.isSynchronizedWithTransaction()) { 
     6       //hodler保存的执行类型和获取SqlSession的执行类型不一致,就会抛出异常,也就是说在同一个事务中,执行类型不能变化,原因就是同一个事务中同一个sqlSessionFactory创建的sqlSession会被重用 
     7       if (holder.getExecutorType() != executorType) { 
     8         throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction"); 
     9       } 
    10       //增加该holder,也就是同一事务中同一个sqlSessionFactory创建的唯一sqlSession,其引用数增加,被使用的次数增加 
    11       holder.requested(); 
    12    //返回sqlSession 
    13       return holder.getSqlSession(); 
    14     } 
    15  //如果找不到,则根据执行类型构造一个新的sqlSession 
    16     SqlSession session = sessionFactory.openSession(executorType); 
    17  //判断同步是否激活,只要SpringTX被激活,就是true 
    18     if (isSynchronizationActive()) { 
    19    //加载环境变量,判断注册的事务管理器是否是SpringManagedTransaction,也就是Spring管理事务 
    20       Environment environment = sessionFactory.getConfiguration().getEnvironment(); 
    21       if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) { 
    22   //如果是,则将sqlSession加载进事务管理的本地线程缓存中 
    23         holder = new SqlSessionHolder(session, executorType, exceptionTranslator); 
    24   //以sessionFactory为key,hodler为value,加入到TransactionSynchronizationManager管理的本地缓存ThreadLocal<Map<Object, Object>> resources中 
    25         bindResource(sessionFactory, holder); 
    26   //将holder, sessionFactory的同步加入本地线程缓存中ThreadLocal<Set<TransactionSynchronization>> synchronizations 
    27         registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory)); 
    28         //设置当前holder和当前事务同步 
    29   holder.setSynchronizedWithTransaction(true); 
    30   //增加引用数 
    31         holder.requested(); 
    32       } else { 
    33         if (getResource(environment.getDataSource()) == null) { 
    34         } else { 
    35           throw new TransientDataAccessResourceException( 
    36               "SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization"); 
    37         } 
    38       } 
    39     } else { 
    40     } 
    41     return session; 
    42   } 
    复制代码
    复制代码
     1 public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) { 
     2  //其实下面就是判断session是否被Spring事务管理,如果管理就会得到holder  
     3     SqlSessionHolder holder = (SqlSessionHolder) getResource(sessionFactory); 
     4     if ((holder != null) && (holder.getSqlSession() == session)) { 
     5    //这里释放的作用,不是关闭,只是减少一下引用数,因为后面可能会被复用 
     6       holder.released(); 
     7     } else { 
     8    //如果不是被spring管理,那么就不会被Spring去关闭回收,就需要自己close 
     9       session.close(); 
    10     } 
    11   } 
    复制代码

    其实通过上面的代码我们可以看出 Mybatis在很多地方都用到了代理模式,这个模式可以说是一种经典模式,

    其实不紧紧在Mybatis当中使用广泛,Spring的事物,AOP ,连接池技术 等技术都使用了代理技术。在后面的文章中我们来分析Spring的抽象事物管理机制。

  • 相关阅读:
    1010每次备份我的MySQL数据库
    1008win7与虚拟机中的linux共享文件的(详细)方法
    0930MySQL中实现高性能高并发计数器方案(例如文章点击数)
    0929shell操作mysql
    0929mysql前缀索引如何找到合适的位数
    0929mysql 用户管理和权限设置
    学习笔记之机器学习实战 (Machine Learning in Action)
    学习笔记之Python for Data Analysis
    学习笔记之入行数据科学,这些书一定要看
    面试总结之Python
  • 原文地址:https://www.cnblogs.com/longxok/p/10881931.html
Copyright © 2011-2022 走看看