目录
- 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来看看
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
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的所有操作
public int insert(String statement, Object parameter) { | |
return this.sqlSessionProxy.insert(statement, parameter); | |
} | |
········· |
可以看出来,SqlSessionTemplate就是对SqlSession套了个壳子,具体实现还是有代理的sqlSessionProxy去执行
接下类进入
SqlSessionInterceptor
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
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对象和线程进行绑定,对象本身可以循环被一个线程反复使用,但是依然保证了线程安全,在事务提交/回滚需要清理缓存,交换连接给数据库连接池