java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Mybatis SqlSessionFactory

Mybatis SqlSessionFactory与SqlSession详细讲解

作者:氵奄不死的鱼

SqlSessionFactory是MyBatis的核心类之一,其最重要的功能就是提供创建MyBatis的核心接口SqlSession,所以我们需要先创建SqlSessionFactory,为此我们需要提供配置文件和相关的参数

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(); 
}

创建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();
  }

经过一系列设置创建出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);
    }
  }

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());
  }

内部的sqlSessionProxy是对sqlSession做的代理

处理类是SqlSessionInterceptor

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

 @Override
  public int insert(String statement, Object parameter) {
    return this.sqlSessionProxy.insert(statement, parameter);
  }
·········

可以看出来,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;
      } 
  }

先获取一个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);

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(); 
}

创建一个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();
    }
  }

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

  public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
  }

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();
    }
  }

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

SqlSession生命周期

如果说SqlSessionFactory相当于数据库连接池,那么SqlSession就相当于一个数据库连接(Connection对象),

你可以在一个事务里面执行多条SQL,然后通过它的commit、rollback等方法,提交或者回滚事务。所以它应该存活在一个业务请求中,

处理完整个请求后,应该关闭这条连接,让它归还给SqlSessionFactory,否则数据库资源就很快被消耗精光,系统应付瘫痪,所以用try…catch…fanally语句来保证其正确关闭。

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

到此这篇关于Mybatis SqlSessionFactory与SqlSession详细讲解的文章就介绍到这了,更多相关Mybatis SqlSessionFactory内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文