目录
- SqlssionFactory
- 创建SqlSessionFactory
- SqlSessionTemplate
- SqlSessionInterceptor
- SqlSession
- 创建一个SqlSession
- 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();
}
创建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对象和线程进行绑定,对象本身可以循环被一个线程反复使用,但是依然保证了线程安全,在事务提交/回滚需要清理缓存,交换连接给数据库连接池