mybatis SqlSession,SqlSessionFactory及spring SqlSessionTemplate

基本概念

  • SqlSession:
    • 数据库CRUD及事务操作接口
    • 线程不安全,常用于Request范围或method范围
// Request范围,4次sql执行共用一个SqlSession
sqlSessionManager.startManagedSession();
try {
    sqlSessionManager.query1();
    sqlSessionManager.query2();
    sqlSessionManager.update1();
    sqlSessionManager.update2();
    //...
}catch (Throwable t) {
    sqlSessionManager.rollback();
} finally {
    sqlSessionManager.close();
}
// 方法范围,两次调用创建了两个session
SqlSessionManager.query1();
SqlSessionManager.query2();
  • SqlSessionFactory
    • 创建SqlSession的工厂类
  • SqlSessionManager/SqlSessionTemplate[装饰器模式]
    • SqlSession装饰器
    • 增强SqlSession生命周期管理
      这里写图片描述

SqlSessionManager/SqlSessionTemplate源码分析

public class SqlSessionManager implements SqlSessionFactory, SqlSession {
    private final SqlSessionFactory sqlSessionFactory;
    // SqlSession动态代理,增强SqlSession生命周期管理
    private final SqlSession sqlSessionProxy;

    // 线程局部变量localSqlSession,每个线程内部都持有一个sqlSession副本,互不干扰
    private final ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<SqlSession>();

    private SqlSessionManager(SqlSessionFactory sqlSessionFactory) {
        this.sqlSessionFactory = sqlSessionFactory;
        // 创建代理对象SqlSessionProxy
        this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
                SqlSessionFactory.class.getClassLoader(),
                new Class[]{SqlSession.class},
                new SqlSessionInterceptor());
    }

    private class SqlSessionInterceptor implements InvocationHandler {

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get();
            if (sqlSession != null) {
                // 如果线程内已存在sqlSession,直接共用,方法调用后由程序员手动关闭
                try {
                    return method.invoke(sqlSession, args);
                } catch (Throwable t) {
                    throw ExceptionUtil.unwrapThrowable(t);
                }
            } else {
                // 1. 创建方法级sqlSession
                final SqlSession autoSqlSession = openSession();
                try {
                    // 2. 调用SqlSession方法执行sql
                    final Object result = method.invoke(autoSqlSession, args);
                    // 3.1 执行成功,提交事务
                    autoSqlSession.commit();
                    return result;
                } catch (Throwable t) {
                    // 3.2 执行异常,回滚事务
                    autoSqlSession.rollback();
                    throw ExceptionUtil.unwrapThrowable(t);
                } finally {
                    // 4. 关闭SqlSession
                    autoSqlSession.close();
                }
            }
        }
    }
}
/**
 * 1. Thread safe, Spring managed, {@code SqlSession} that works with Spring
 * transaction management to ensure that that the actual SqlSession used is the
 * one associated with the current Spring transaction. 
 * spring管理,线程安全,确保SqlSession是当前spring事务的sqlSession
 * 2. SqlSession生命周期管理,包括commit, rollback, close
 * 3. 单例,所有DAO可以共用一个
 */
public class SqlSessionTemplate implements SqlSession, DisposableBean {

    private final SqlSessionFactory sqlSessionFactory;

    private final ExecutorType executorType;

    private final SqlSession sqlSessionProxy;

    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;
        this.sqlSessionProxy = (SqlSession) newProxyInstance(
                SqlSessionFactory.class.getClassLoader(),
                new Class[] { SqlSession.class },
                new SqlSessionInterceptor());
    }

    private class SqlSessionInterceptor implements InvocationHandler {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            SqlSession sqlSession = getSqlSession(
                    SqlSessionTemplate.this.sqlSessionFactory,
                    SqlSessionTemplate.this.executorType,
                    SqlSessionTemplate.this.exceptionTranslator);
            try {
                Object result = method.invoke(sqlSession, args);
                // 非事务的sqlSession,执行完立即commit
                if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
                    sqlSession.commit(true);
                }
                return result;
            } catch (Throwable t) {
                Throwable unwrapped = unwrapThrowable(t);
                if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
                    // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
                    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);
                }
            }
        }
    }
}

/**
   * 1. 非事务SqlSession,close
   * 2. 事务sqlsession, 更新引用计数器,当计数器为0,即事务结束时,spring调用close callback
   */
  public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
    ......
  }
  • Spring SqlSessionTemplate SqlSession生命周期管理
    • SqlSessionTemplate与SqlSessionManager实现SqlSession生命周期管理的方式相同
    • SqlSessionManager由使用者决定是否共用SqlSession
    • SqlSessionTemplate由Spring事务管理器决定是否共用SqlSession。
      • 事务内部共用SqlSession
      • 非事务不共用SqlSession

Spring创建SqlSessionFactory, SqlSessionTemplate, SqlSession流程

spring配置

<!-- define the SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="configLocation" value="classpath:mybatis-config.xml" />
</bean>

<!-- scan for mappers and let them be autowired -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="*.dao" />
</bean>

Spring生成MapperProxy bean流程图
MapperFactoryBean生成MapperProxy bean时序图

MapperProxy

  • DAO动态代理类:拦截DAO方法调用,调用SqlSession执行SQL,返回结果
  • Spring中采用SqlSessionTemplate执行SQL,对SqlSession的生命周期管理进行增强
// 拦截器
public class MapperProxy<T> implements InvocationHandler, Serializable {
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache;

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    // 执行sql,处理结果,并返回
    return mapperMethod.execute(sqlSession, args);
  }
  ......
}

// MapperProxyFactory newInstance()生成动态代理对象
Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, new MapperProxy<T>(sqlSession, mapperInterface, methodCache))

JAVA动态代理

  • 详见代理模式(暂无)

猜你喜欢

转载自blog.csdn.net/weixin_41810396/article/details/81840134