MyBatis核心类
SqlSessionFactory
每一个MyBatis应用都是以一个SqlSessionFactory
的实例为核心构建的。SqlSessionFactory
的核心作用是什么?
从类的名称上可以看出来,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
的实例如何创建呢?
1、通常我们是在只有MyBatis的项目中是使用下面的代码段来创建的:
String resource = "org/mybatis/example/mybatis-config.xml";
//读取mybatis配置的问题
InputStream inputStream = Resources.getResourceAsStream(resource);
//通过SqlSessionFactoryBuilder的build方式创建
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
2、在Spring boot和MyBatis结合的项目中我会使用下面的代码段来创建:
MyBatis-Spring-Boot-Starter依赖将会提供如下
- 自动检测现有的DataSource
- 将创建并注册SqlSessionFactory的实例,该实例使用SqlSessionFactoryBean将该DataSource作为输入进行传递
- 将创建并注册从SqlSessionFactory中获取的SqlSessionTemplate的实例。
- 自动扫描您的mappers,将它们链接到SqlSessionTemplate并将其注册到Spring上下文,以便将它们注入到您的bean中。
- 就是说,使用了该Starter之后,只需要定义一个DataSource即可(application.properties中可配置),它会自动创建使用该DataSource的SqlSessionFactory Bean以及SqlSessionTemplate。会自动扫描你的Mappers,连接到SqlSessionTemplate,并注册到Spring上下文中.
@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里主要就是解析资源的内容,然后进行创建。
在这里有分别使用了两个设计模式:
- 工厂模式
SqlSessionFactory
就是一个工厂模式——简单工厂模式的变形实现。
通过SqlSessionFactory
中重载的不同openSession()方法来获取不同类型的实例。
- 建造者模式(build模式)
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);
}
SqlSession
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 或映射器所共享使用。
因为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()实现?
好难啊~~~~~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。