Mybatis 常用的几个对象

wulunyong 8年前

来自: http://my.oschina.net/heweipo/blog/616318


1、SqlSessionFactoryBuilder

这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。因此 SqlSessionFactoryBuilder 实例的最佳范围是方法范围(也就是局部方法变量)。你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但是最好还是不要让其一直存在以保证所有的 XML 解析资源开放给更重要的事情。那么SqlSessionFactoryBuilder一旦使用它创建完了SqlSessionFactory 就可以销毁了,这样也可以保证整个应用只有一个SqlSessionFactory 

2、SqlSessionFactory

每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先定制的 Configuration 的实例构建出 SqlSessionFactory 的实例。

可以从 XML 文件中构建 SqlSessionFactory ,也可以从 Java 程序而不是 XML 文件中创建。

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由对它进行清除或重建。使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏味道(bad smell)”。因此 SqlSessionFactory 的最佳范围是应用范围。有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

 

3、SqlSession

每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的范围是请求或方法范围。绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。也绝不能将 SqlSession 实例的引用放在任何类型的管理范围中,比如 Serlvet 架构中的 HttpSession。如果你现在正在使用一种 Web 框架,要考虑 SqlSession 放在一个和 HTTP HttpRequest请求对象相似的范围中。换句话说,每次收到的 HTTP 请求,就可以打开一个 SqlSession,返回一个响应,就关闭它。这个关闭操作是很重要的,你应该把这个关闭操作放到 finally 块中以确保每次都能执行关闭。下面的示例就是一个确保 SqlSession 关闭的标准模式:

SqlSession session = sqlSessionFactory.openSession();

try {

  // do work

} finally {

  session.close();

}

在你的所有的代码中一致性地使用这种模式来保证所有数据库资源都能被正确地关闭。

Spring 依赖注入框架可以创建线程安全的、基于事务的 SqlSession 和映射器(mapper)并将它们直接注入到你的 bean 中,因此它们的生命周期将交给Spring管理。

4、SqlSessionTemplate

他是org.mybatis.spring包下面的类,而且他还实现sqlSession,但是他sqlSession的操作都是由他内部的成员sqlSessionProxy处理完成的,这个对象的实例其实是DefaultSqlSession。无论如何,他是Spring相关的,他的生命周期应该由Spring的来管理,那么涉及到 sqlSession的管理,事务的隔离,甚至缓存的控制。这些内部都已经处理完成,我们使用时只需要直接调用 SqlSessionTemplate 的数据操作方法,对于session的关闭,我们不需要担心,另外,虽然 SqlSessionTemplate 是在Spring采用构造方法的方式注入,默认就是一个单例,但是我们在应用当中,实际每次一个事务我们都会实例化一个DefaultSqlSession,而且处理完之后也会关闭,如果一个事务中有多个数据操作,那么他们使用的是同一个sqlsession。对于一个拥有事务的sqlsession,他的关闭时期是在事务提交的时候关闭,换句话说,关闭sqlsession是事务应该负责的,但是如果没有被事务管理,你们自己进行关闭。

每次执行数据操作时获取sqlsession的代码:

SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);  SqlSession session = sessionHolder(executorType, holder);  if(session != null) {      return session; // 如果已经存在,那么直接返回  } else {      if(LOGGER.isDebugEnabled()) {          LOGGER.debug("Creating a new SqlSession");      }      session = sessionFactory.openSession(executorType); // 如果不存在,应该生成,而且存放在SessionHolder中管理      registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);      return session;  }

 

代理执行数据操作的代码

SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator); // 获取DefaultSqlSession  Object unwrapped;// 返回值,会默认被缓存在内存中(一级缓存)  try {      Object t = method.invoke(sqlSession, args); // 代理调用数据操作方法      if(!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {          sqlSession.commit(true); // 判断这个sqlsession是否是被事务容器所管理,没有被管理则自己提交,被管理则事务提交      }      unwrapped = t;  } catch (Throwable var11) {      unwrapped = ExceptionUtil.unwrapThrowable(var11);      if(SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {          SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);// 关闭sqlsession          sqlSession = null;          DataAccessException 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); // 关闭sqlsession      }    }