每个MyBatis应用都是以一个SqlSessionFactory
的实例为核心构建的。SqlSessionFactory
的核心做用是什么?sql
从类的名称上能够看出来,SqlSessionFactory
是产生SqlSession
的工厂。SqlSessionFactory
是经过SqlSessionFactoryBuilder
这个构建器来构建的。
SqlSessionFactory
是一个接口,其中定义了获取SqlSession
的方法。数据库
public interface SqlSessionFactory { //获取一个SqlSession SqlSession openSession(); //获取一个SqlSession,参数设置事务是否自动提交 SqlSession openSession(boolean var1); //经过指定Connection中的参数获取 SqlSession openSession(Connection var1); //获取SqlSession,设置事务的隔离级别,NONE(0),READ_COMMITTED(2),READ_UNCOMMITTED(1),REPEATABLE_READ(4),SERIALIZABLE(8); SqlSession openSession(TransactionIsolationLevel var1); //获取SqlSession,同时制定执行的类别,支持三种SIMPLE(这种模式下,将为每一个语句建立一个PreparedStatement),REUSE(这个模式下重复使用preparedStatment),BATCH(批量更新,insert时候,若是没有提交,没法获取自增id); SqlSession openSession(ExecutorType var1); SqlSession openSession(ExecutorType var1, boolean var2); SqlSession openSession(ExecutorType var1, TransactionIsolationLevel var2); SqlSession openSession(ExecutorType var1, Connection var2); //获取全部的配置项 Configuration getConfiguration(); }
SqlSessionFactory
包含两个实现:DefaultSqlSessionFactory
和SqlSessionManager
SqlSessionFactory
的实例如何建立呢?
一、一般咱们是在只有MyBatis的项目中是使用下面的代码段来建立的:设计模式
String resource = "org/mybatis/example/mybatis-config.xml"; //读取mybatis配置的问题 InputStream inputStream = Resources.getResourceAsStream(resource); //经过SqlSessionFactoryBuilder的build方式建立 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
二、在Spring boot和MyBatis结合的项目中我会使用下面的代码段来建立:缓存
MyBatis-Spring-Boot-Starter依赖将会提供以下
@Bean public SqlSessionFactory sqlSessionFactory(@Qualifier("druidDataSource") DataSource druidDataSource) throws Exception { //先建立一个SqlSessionFactoryBean SqlSessionFactoryBean fb = new SqlSessionFactoryBean(); //在这个bean里设置须要的参数 fb.setDataSource(this.dynamicDataSource(druidDataSource)); fb.setTypeAliasesPackage(env.getProperty("mybatis.type-aliases-package")); fb.setTypeHandlersPackage(env.getProperty("mybatis.type-handlers-package")); fb.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(env.getProperty("mybatis.mapperLocations"))); //经过这个方法获取一个SqlSessionFactory return fb.getObject(); }
在代码里一直向下查找的时候就会发现:这个过程其实和上面的过程同样。
SqlSessionFactoryBean会将一系列的属性封装成一个Configuration对象,而后调用this.sqlSessionFactoryBuilder.build(configuration)
来建立。而在sqlSessionFactoryBuilder里主要就是解析资源的内容,而后进行建立。安全
在这里有分别使用了两个设计模式:session
SqlSessionFactory
就是一个工厂模式——简单工厂模式的变形实现。
经过SqlSessionFactory
中重载的不一样openSession()方法来获取不一样类型的实例。mybatis
SqlSessionFactoryBuilder
建立SqlSessionFactory
就是建造者模式的实现。在建立的过程当中须要解析不少的文件,生成对象,进行缓存等操做,因此一个方法是很难直接写完,因此其中应用了大量的build模式:多线程
protected SqlSessionFactory buildSqlSessionFactory() throws IOException { XMLConfigBuilder xmlConfigBuilder = null; Configuration configuration; if (this.configuration != null) { ...... } else if (this.configLocation != null) { xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), (String)null, this.configurationProperties); configuration = xmlConfigBuilder.getConfiguration(); } else { ...... } ...... if (xmlConfigBuilder != null) { try { //builder解析 xmlConfigBuilder.parse(); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'"); } } catch (Exception var22) { throw new NestedIOException("Failed to parse config resource: " + this.configLocation, var22); } finally { ErrorContext.instance().reset(); } } ...... if (!ObjectUtils.isEmpty(this.mapperLocations)) { Resource[] var29 = this.mapperLocations; var27 = var29.length; for(var5 = 0; var5 < var27; ++var5) { Resource mapperLocation = var29[var5]; if (mapperLocation != null) { try { //Mapper文件build XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), configuration, mapperLocation.toString(), configuration.getSqlFragments()); xmlMapperBuilder.parse(); } catch (Exception var20) { } finally { //单例模式 ErrorContext.instance().reset(); } } } } //build一个对象返回 return this.sqlSessionFactoryBuilder.build(configuration); }
SqlSessionFactory
建立完成以后,就能够经过SqlSessionFactory
对象来获取一个SqlSession
实例.SqlSession
是一次与数据库的会话.在他的接口中定义了一些列的CRUD和事务的操做接口。SqlSession
是暴露给用户使用的API,一个SqlSession
对应着一次数据库会话。SqlSession
不是线程安全的,因此在使用的时候必定要保证他是局部变量。
他对应的类图以下:SqlSession
有几种常见的实现:DefaultSqkSession
是默认的非线程安全的实现,SqlSessionManager
是Mybatis中对SqlSession
的线程安全实现,在内部是使用的private ThreadLocal<SqlSession> localSqlSession = new ThreadLocal();
的形式来保证线程安全的,SqlSessionTemplate
是MyBatis-Spring 的核心。做为 SqlSession 的一个实现,这意味着可使用它无缝代替你代码中已经在使用的 SqlSession。SqlSessionTemplate 是线程安全的,能够被多个 DAO 或映射器所共享使用。app
由于SqlSessionTemplate
是线程安全的,因此当SqlSessionTemplate
是单例的时候,多线程调用SqlSessionTemplate
仍然使用的是同一个SqlSession
,接下来看一下SqlSessionTemplate
是如何保证线程安全的呢?函数
首先咱们看一下SqlSessionTemplate
的建立过程:
/** * 构造函数1,须要传入参数SqlSessionFactory */ public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) { this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType()); } public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) { this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true)); } 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; //建立代理类的实例,该代理类实现了SqlSession接口,这里使用的是基于JDK的动态代理,SqlSessionInterceptor也是实现了InvocationHandler接口,最后代理对象的操做都会通过invoke执行 //class SqlSessionInterceptor implements InvocationHandler this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionTemplate.SqlSessionInterceptor()); }
invoke的实现是实现线程安全的核心:
private class SqlSessionInterceptor implements InvocationHandler { private SqlSessionInterceptor() { } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //获取真实的SqlSession,这个是真实使用的,是MyBatis生成的DefaultSqlSession,获取是线程安全实现的核心 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; } }
接着看getSqlSession()的代码:
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { Assert.notNull(sessionFactory, "No SqlSessionFactory specified"); Assert.notNull(executorType, "No ExecutorType specified"); //SqlSessionHolder是对SqlSession的一个功能包装,TransactionSynchronizationManager是一个事务同步管理器,维护当前线程事务资源,信息以及TxSync集合,getResource会从 ThreadLocal<Map<Object, Object>> resources 中获取当前线程SqlSessionHolder实例 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"); } //若是没有获取成功,那么开启一个SqlSession session = sessionFactory.openSession(executorType); //注册SessionHolder registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session); return session; } }
registerSessionHolder()实现?
好难啊~~~~~