• SqlSessionFactory与SqlSession


    SqlssionFactory

    1.SqlSessionFactory是MyBatis的关键对象,它是个单个数据库映射关系经过编译后的内存镜像。
    2.SqlSessionFactory对象的实例可以通过SqlSessionFactoryBuilder对象类获得,而SqlSessionFactoryBuilder则可以从XML配置文件或一个预先定制的Configuration的实例构建出SqlSessionFactory的实例。
    3.每一个MyBatis的应用程序都以一个SqlSessionFactory对象的实例为核心。
    4.SqlSessionFactory是线程安全的,SqlSessionFactory一旦被创建,应该在应用执行期间都存在。在应用运行期间不要重复创建多次,建议使用单例模式。
    5.SqlSessionFactory是创建SqlSession的工厂。
    SqlSessionFactory接口源码如下所示

    package org.apache.ibatis.session; 
    
    import java.sql.Connection; 
    
    public interface SqlSessionFactory { 
      SqlSession openSession();//这个方法最经常用,用来创建SqlSession对象。
      SqlSession openSession(boolean autoCommit); 
      SqlSession openSession(Connection connection); 
      SqlSession openSession(TransactionIsolationLevel level); 
      SqlSession openSession(ExecutorType execType); 
      SqlSession openSession(ExecutorType execType, boolean autoCommit); 
      SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level); 
      SqlSession openSession(ExecutorType execType, Connection connection); 
      Configuration getConfiguration(); 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    创建SqlSessionFactory

    .mybatis框架主要是围绕着SqlSessionFactory进行的,创建过程大概如下:
    就从mybatis默认实现的MybatisAutoConfiguration来看看

     @Bean
      @ConditionalOnMissingBean
      public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        //创建SqlSessionFactoryBean对象
         SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
          //设置数据源
        factory.setDataSource(dataSource);
        factory.setVfs(SpringBootVFS.class);
        if (StringUtils.hasText(this.properties.getConfigLocation())) {
         //mybatis ,Xml配置文件路径
            、factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
        }
          //应用配置文件
        applyConfiguration(factory);
          
          //Mybatis额外的配置文件
        if (this.properties.getConfigurationProperties() != null) {
          factory.setConfigurationProperties(this.properties.getConfigurationProperties());
        }
          //设置mybatis拦截器
        if (!ObjectUtils.isEmpty(this.interceptors)) {
          factory.setPlugins(this.interceptors);
        }
        ···
          //设置*mapper.xml扫描路径
        if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
          factory.setMapperLocations(this.properties.resolveMapperLocations());
        }
        ····	
    
            //获取创建SqlSessionFactoryBean对象
        return factory.getObject();
      }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    经过一系列设置创建出SqlSessionFactory
    注意仅有当数据源只有一个实现时,MybatisAutoConfiguration才会生效,如果时多数据源的情况下,那么需要自己编写定义SqlSessionFactory的创建逻辑

    SqlSessionTemplate

    SqlSessionTemplate是线程安全的,生命周期由spring管理的,同spring事务一起协作来保证真正执行的SqlSession是在spring的事务中的一个SqlSession的实现类
    创建SqlSessionTemplate

    @Bean
      @ConditionalOnMissingBean
      public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        ExecutorType executorType = this.properties.getExecutorType();
        if (executorType != null) {
          return new SqlSessionTemplate(sqlSessionFactory, executorType);
        } else {
          return new SqlSessionTemplate(sqlSessionFactory);
        }
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    SqlSessionTemplate是作为一个bean被spring管理的

      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());
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    内部的sqlSessionProxy是对sqlSession做的代理
    处理类是SqlSessionInterceptor
    在这里插入图片描述

    SqlSessionTemplate继承自SqlSession,实现了sqlSession的所有操作

     @Override
      public int insert(String statement, Object parameter) {
        return this.sqlSessionProxy.insert(statement, parameter);
      }
    ·········
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    可以看出来,SqlSessionTemplate就是对SqlSession套了个壳子,具体实现还是有代理的sqlSessionProxy去执行
    接下类进入

    SqlSessionInterceptor

     @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);
            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;
          } 
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    先获取一个sqlSession,这里也是sqlSession线程安全的原因
    getSqlSession方法拿到DefaultSqlSession实例,getSqlSession方法里面处理了sqlSession的线程安全问题(通过ThreadLocal实现)。

     SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
    
        SqlSession session = sessionHolder(executorType, holder);
        if (session != null) {
          return session;
        }
    
        LOGGER.debug(() -> "Creating a new SqlSession");
        session = sessionFactory.openSession(executorType);
    
        registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    sqlSession与线程绑定,如果当前线程未绑定sqlSession,会创建并绑定。保证了一个sqlSession一定只能被一个线程使用。
    另外如果在一个事务下,那么肯定是在同一个线程,事务内的操作会共用一个sqlSession,并且,每一步操作完毕之后不会自动提交事务。

    SqlSession

    1.SqlSession是MyBatis的关键对象,是执行持久化操作的独享,类似于JDBC中的Connection。
    2.它是应用程序与持久层之间执行交互操作的一个单线程对象,也是MyBatis执行持久化操作的关键对象。
    3.SqlSession对象完全包含以数据库为背景的所有执行SQL操作的方法,它的底层封装了JDBC连接,可以用SqlSession实例来直接执行被映射的SQL语句。
    4.每个线程都应该有它自己的SqlSession实例。
    5.SqlSession的实例不能被共享,同时SqlSession也是线程不安全的,绝对不能讲SqlSeesion实例的引用放在一个类的静态字段甚至是实例字段中。也绝不能将SqlSession实例的引用放在任何类型的管理范围中,比如Servlet当中的HttpSession对象中。
    6.使用完SqlSeesion之后关闭Session很重要,应该确保使用finally块来关闭它。

    package org.apache.ibatis.session; 
    import java.io.Closeable; 
    import java.sql.Connection; 
    import java.util.List; 
    import java.util.Map; 
    import org.apache.ibatis.executor.BatchResult; 
    public interface SqlSession extends Closeable { 
      <T> T selectOne(String statement); 
      <T> T selectOne(String statement, Object parameter); 
      <E> List<E> selectList(String statement); 
      <E> List<E> selectList(String statement, Object parameter); 
      <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds); 
      <K, V> Map<K, V> selectMap(String statement, String mapKey); 
      <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey); 
      <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds); 
      void select(String statement, Object parameter, ResultHandler handler); 
      void select(String statement, ResultHandler handler); 
      void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler); 
      int insert(String statement); 
      int insert(String statement, Object parameter); 
      int update(String statement); 
      int update(String statement, Object parameter); 
      int delete(String statement); 
      int delete(String statement, Object parameter); 
      void commit(); void commit(boolean force); 
      void rollback(); 
      void rollback(boolean force); 
      List<BatchResult> flushStatements(); 
      void close(); 
      void clearCache(); 
      Configuration getConfiguration(); 
      <T> T getMapper(Class<T> type); 
      Connection getConnection(); 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    创建一个SqlSession

    看了前面我们知道了sqlSession通过SqlSessionTemplate进行创建的
    org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromDataSource

     private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;
        try {
          final Environment environment = configuration.getEnvironment();
          final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
          tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
          final Executor executor = configuration.newExecutor(tx, execType);
          return new DefaultSqlSession(configuration, executor, autoCommit);
        } catch (Exception e) {
          closeTransaction(tx); // may have fetched a connection so lets call close()
          throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    创建一个事务,创建一个Executor,构造DefaultSqlSession

      public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
        this.configuration = configuration;
        this.executor = executor;
        this.dirty = false;
        this.autoCommit = autoCommit;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    dirty字段代表如果在在未提交之前有更新操作,会被更新未true,这时调用sqlSession的commit/rollbacl方法
    调用 executor.commit都传的是true

    @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();
        }
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    后续sqlSession的增删改查操作都通过executor完成,executor后面再继续研究

    SqlSession生命周期

    如果说SqlSessionFactory相当于数据库连接池,那么SqlSession就相当于一个数据库连接(Connection对象),
    你可以在一个事务里面执行多条SQL,然后通过它的commit、rollback等方法,提交或者回滚事务。所以它应该存活在一个业务请求中,
    处理完整个请求后,应该关闭这条连接,让它归还给SqlSessionFactory,否则数据库资源就很快被消耗精光,系统应付瘫痪,所以用try…catch…fanally语句来保证其正确关闭。

    实际上MyBatis整合springBoot的情况下,SqlSession对象和线程进行绑定,对象本身可以循环被一个线程反复使用,但是依然保证了线程安全,在事务提交/回滚需要清理缓存,交换连接给数据库连接池

  • 相关阅读:
    在MAC安装Lazarus & 起点 - 我们的第一个Lazarus程序!
    最新gcc下载和linux环境变量设置
    【论文翻译】增强复制状态机的两阶段提交协议
    .Net CLR GC 动态加载短暂堆阈值的计算及阈值超量的计算
    vue + video.js 加载多种视频流(HLS、FLV、RTMP、RTSP)
    C#代码审计实战+前置知识
    创业思维和商业模式的区别,从0到1:构建到突破,专业新零售全案策划
    中间件之Nginx的反向代理
    Git 2.37 发布,带来重大变化!!
    前端 js 之 面向对象(原型、原型链及继承) 06
  • 原文地址:https://blog.csdn.net/qq_37436172/article/details/127601927