spring事务源码分析结合mybatis源码(三)
下面将结合mybatis源码来分析下,这种持久化框架是如何对connection使用,来达到spring事务的控制。
想要在把mybatis跟spring整合都需要这样一个jar包:mybatis-spring-x.x.x.jar,这里面定义了一些主要的整合信息。
在spring配置文件中需要配置如下两个bean:
<!-- mybatis配置 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dynamicDataSource" />
<property name="configLocation" value="classpath:mybatis.xml"></property>
<!-- mybatis配置文件 -->
<property name="mapperLocations" value="classpath:com/blackbread/dao/mapper/*.xml" />
</bean>
<!--mapper scanning -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.blackbread.dao" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
首先让我们来看org.mybatis.spring.SqlSessionFactoryBean,在这个类需要注入跟之前tx配置中一样的dataSource。
SqlSessionFactoryBean类实现了InitializingBean接口,所以会执行afterPropertiesSet方法,在afterPropertiesSet方法中会执行buildSqlSessionFactory方法生成一个sqlSessionFactory对象,让我们看下buildSqlSessionFactory方法:由于主要看的是跟spring tx结合的方式,所以代码看不上很细,如有疏漏,望不吝赐教。
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
Configuration configuration;
XMLConfigBuilder xmlConfigBuilder = null;
//初始化一个configuration
if (this.configLocation != null) {
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
} else {
configuration = new Configuration();
configuration.setVariables(this.configurationProperties);
}
if (this.objectFactory != null) {
configuration.setObjectFactory(this.objectFactory);
}
if (this.objectWrapperFactory != null) {
configuration.setObjectWrapperFactory(this.objectWrapperFactory);
}
if (hasLength(this.typeAliasesPackage)) {
String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeAliasPackageArray) {
configuration.getTypeAliasRegistry().registerAliases(packageToScan,
typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
}
}
//设置别名
if (!isEmpty(this.typeAliases)) {
for (Class<?> typeAlias : this.typeAliases) {
configuration.getTypeAliasRegistry().registerAlias(typeAlias);
}
}
//装入插件,mybatis的插件都是以拦截器的形式进行的好像,比如分页插件,这里是载入spring中注入的
if (!isEmpty(this.plugins)) {
for (Interceptor plugin : this.plugins) {
configuration.addInterceptor(plugin);
}
}
if (hasLength(this.typeHandlersPackage)) {
String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeHandlersPackageArray) {
configuration.getTypeHandlerRegistry().register(packageToScan);
}
}
if (!isEmpty(this.typeHandlers)) {
for (TypeHandler<?> typeHandler : this.typeHandlers) {
configuration.getTypeHandlerRegistry().register(typeHandler);
}
}
//这里将解析mybatis.xml文件,载入所有配置,插件、setting等
if (xmlConfigBuilder != null) {
try {
xmlConfigBuilder.parse();
} catch (Exception ex) {
throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
} finally {
ErrorContext.instance().reset();
}
}
//这个很重要,这里定义了用的transactionFactory为SpringManagedTransactionFactory,这个在获取
//connection等地方都有用到,是mybatis跟spring的主要链接
if (this.transactionFactory == null) {
this.transactionFactory = new SpringManagedTransactionFactory();
}
//新建一个Environment对象,并将新建的transactionFactory放入其中
Environment environment = new Environment(this.environment, this.transactionFactory, this.dataSource);
configuration.setEnvironment(environment);
if (this.databaseIdProvider != null) {
try {
configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException e) {
throw new NestedIOException("Failed getting a databaseId", e);
}
}
if (!isEmpty(this.mapperLocations)) {
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
}
try {
//这里主要是解析配置的sql mapper配置文件
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
configuration, mapperLocation.toString(), configuration.getSqlFragments());
xmlMapperBuilder.parse();
} catch (Exception e) {
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
} finally {
ErrorContext.instance().reset();
}
}
} else {
}
}
return this.sqlSessionFactoryBuilder.build(configuration);
}
看着好长的一段啊,其实这里做的工作就是解析配置文件生成configuration对象而已。在
xmlMapperBuilder.parse();
这里将解析sql mapper文件中的映射关系生成MappedStatement对象,并执行 configuration.addMappedStatement(statement);放入到configuration对象中,有兴趣的同学可以仔细看下。
这里主要需要注意的一块就是this.transactionFactory = new SpringManagedTransactionFactory();这里就是mybatis跟spring连接到一起的地方。
接着我们看一下org.mybatis.spring.mapper.MapperScannerConfigurer对象的初始化过程,这个对象实现了BeanDefinitionRegistryPostProcessor接口,在postProcessBeanDefinitionRegistry方法中初始化一个对象ClassPathMapperScanner,并讲执行scan--->doScan方法,
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
for (BeanDefinitionHolder holder : beanDefinitions) {
GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
//实际就是将扫描到的接口包装成MapperFactoryBean的实现类
definition.setBeanClass(MapperFactoryBean.class);
definition.getPropertyValues().add("addToConfig", this.addToConfig);
boolean explicitFactoryUsed = false;
//注入sqlSessionFactory对象,这个也很重要
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
}
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionTemplate != null) {
if (explicitFactoryUsed) {
logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
if (!explicitFactoryUsed) {
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
return beanDefinitions;
}
这段代码其实主要就是从basePackage中扫描到相应的接口类,并且注册到spring中,并且定义此对象的FactoryBean为:MapperFactoryBean,将返回如下对象
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}
最终其实就是生成Handler为MapperProxy的基于mapperInterface的代理类。
同时添加属性:sqlSessionFactory。
这个操作很重要,在后面有关联操作。
这里让我们看下MapperFactoryBean类,这个类继承自SqlSessionDaoSupport而在SqlSessionDaoSupport中有如下方法:
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (!this.externalSqlSession) {
this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
}
}
也就是上面调用的添加sqlSessionFactory属性的set操作,在这个方法中初始话sqlSession,利用的是SqlSessionTemplate对象。
接下来让我们看下SqlSessionTemplate的初始化过程:
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());
}
SqlSessionTemplate其实是实现了SqlSession接口的,在初始化的时候将生成一个sqlSessionProxy 代理类,可以查看下SqlSessionTemplate里面的所有与数据库相关的操作都是通过sqlSessionProxy 这个代理类实现的。
接着看下sqlSessionProxy 的实际handler:
private class SqlSessionInterceptor implements InvocationHandler {
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)) {
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
这里首先需要获取一个SqlSession对象:
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
if (holder != null && holder.isSynchronizedWithTransaction()) {
if (holder.getExecutorType() != executorType) {
throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction");
}
holder.requested();
return holder.getSqlSession();
}
SqlSession session = sessionFactory.openSession(executorType);
if (TransactionSynchronizationManager.isSynchronizationActive()) {
Environment environment = sessionFactory.getConfiguration().getEnvironment();
if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
TransactionSynchronizationManager.bindResource(sessionFactory, holder);
TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));
holder.setSynchronizedWithTransaction(true);
holder.requested();
} else {
if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) {
} else {
throw new TransientDataAccessResourceException(
"SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
}
}
} else {
}
return session;
}
这里将会获取一个SqlSessionHolder并判断是否已经存在,如果不存在将会初始化一个新的,我们这里只分析第一次调用过程,也就是将会执行
SqlSession session = sessionFactory.openSession(executorType);
--->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, autoCommit);
return new DefaultSqlSession(configuration, executor);
} 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();
}
}
这里是主要的跟spring结合部分,让我们仔细分析下,首先这里将获取TransactionFactory: final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);这里将得到我们之前初始化时候加入的SpringManagedTransactionFactory。然后将初始化当前的tx:
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
然后将生成一个Executor ,将final Executor executor = configuration.newExecutor(tx, execType, autoCommit);这里在之前指定了execType为Simple,所以在这里将生成一个SimpleExecutor: executor = new SimpleExecutor(this, transaction);并将transaction加入属性。
到这里SqlSession的初始化也就完成了,接下来就是通过反射进行实际方法的执行了:
Object result = method.invoke(sqlSession, args);
以一个update操作来说明:
public int update(String statement, Object parameter) {
try {
dirty = true;
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.update(ms, wrapCollection(parameter));
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error updating database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
这里首先将从configuration中根据操作的statement获取映射内容MappedStatement ,
getMappedStatement(String id)---->getMappedStatement(String id, boolean validateIncompleteStatements)
接着将执行executor.update(ms, wrapCollection(parameter)),也就是实际的数据库操作了,记得之前初始化的executor么,这里就是对应的SimpleExecutor
public int update(MappedStatement ms, Object parameter) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
if (closed) throw new ExecutorException("Executor was closed.");
clearLocalCache();
return doUpdate(ms, parameter);
}
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
} finally {
closeStatement(stmt);
}
}
这里主要是看prepareStatement方法:
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection);
handler.parameterize(stmt);
return stmt;
}
然后看Connection 方法:
protected Connection getConnection(Log statementLog) throws SQLException {
Connection connection = transaction.getConnection();
if (statementLog.isDebugEnabled()) {
return ConnectionLogger.newInstance(connection, statementLog);
} else {
return connection;
}
}
到这里看到了真正的Connection获取方法: transaction.getConnection();也就是通过之前注入的transaction中获取connection,而这个transaction也就是对应的SpringManagedTransaction,他的调用过程getConnection()---->openConnection()
private void openConnection() throws SQLException {
this.connection = DataSourceUtils.getConnection(this.dataSource);
this.autoCommit = this.connection.getAutoCommit();
this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource);
}
这里其实就是调用了DataSourceUtils.getConnection(this.dataSource);来获取一个Connection。
看看DataSourceUtils的getConnection(DataSource dataSource)--->doGetConnection(DataSource dataSource)
public static Connection doGetConnection(DataSource dataSource) throws SQLException {
//从TransactionSynchronizationManager中获取ConnectionHolder,这个对象也就是之前我们第一次分析spring tx的时候
//持有ConnectionHolder的对象了
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
//由于在前面的切面中已经开启事务,并且初始化了ConnectionHolder所以这里将直接返回ConnectionHolder中的connection
if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
conHolder.requested();
if (!conHolder.hasConnection()) {
conHolder.setConnection(dataSource.getConnection());
}
return conHolder.getConnection();
}
Connection con = dataSource.getConnection();
if (TransactionSynchronizationManager.isSynchronizationActive()) {
ConnectionHolder holderToUse = conHolder;
if (holderToUse == null) {
holderToUse = new ConnectionHolder(con);
}
else {
holderToUse.setConnection(con);
}
holderToUse.requested();
TransactionSynchronizationManager.registerSynchronization(
new ConnectionSynchronization(holderToUse, dataSource));
holderToUse.setSynchronizedWithTransaction(true);
if (holderToUse != conHolder) {
TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
}
}
return con;
}
是不是感觉这段代码很眼熟,对了,因为这里有我们第一篇里面非常熟悉的TransactionSynchronizationManager,在spring tx中也是通过这个类中的resources (ThreadLocal对象)对ConnectionHolder进行持有的。
在这里将获取到之前持有的ConnectionHolder对象,并从中获取到Connection 对象然后返回,这样就保证了spring tx中控制的Connection 跟实际调用的Connection为同一个Connection,也就可以通过spring tx对事务进行管理了。
后续的对数据的操作有兴趣的可以自己读一下,感觉mybatis的源码没有spring的那么清晰,还是需要仔细分析下才能整合到一起。
看的比较粗略,难免有疏漏地方,望不吝赐教。
spring事务源码分析结合mybatis源码(三)的更多相关文章
- spring事务源码分析结合mybatis源码(一)
最近想提升,苦逼程序猿,想了想还是拿最熟悉,之前也一直想看但没看的spring源码来看吧,正好最近在弄事务这部分的东西,就看了下,同时写下随笔记录下,以备后查. spring tx源码分析 这里只分析 ...
- spring事务源码分析结合mybatis源码(二)
让我们继续上篇,分析下如果有第二个调用进入的过程. 代码部分主要是下面这个: if (isExistingTransaction(transaction)) { return handleExisti ...
- 精尽MyBatis源码分析 - SQL执行过程(三)之 ResultSetHandler
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
- Spring Ioc源码分析系列--Ioc源码入口分析
Spring Ioc源码分析系列--Ioc源码入口分析 本系列文章代码基于Spring Framework 5.2.x 前言 上一篇文章Spring Ioc源码分析系列--Ioc的基础知识准备介绍了I ...
- NIO 源码分析(05) Channel 源码分析
目录 一.Channel 类图 二.begin 和 close 是什么 2.1 AbstractInterruptibleChannel 中的 begin 和 close 2.2 Selector 中 ...
- NIO 源码分析(02-2) BIO 源码分析 Socket
目录 一.BIO 最简使用姿势 二.connect 方法 2.1 Socket.connect 方法 2.2 AbstractPlainSocketImpl.connect 方法 2.3 DualSt ...
- NIO 源码分析(02-1) BIO 源码分析
目录 一.BIO 最简使用姿势 二.ServerSocket 源码分析 2.1 相关类图 2.2 主要属性 2.3 构造函数 2.4 bind 方法 2.5 accept 方法 2.6 总结 NIO ...
- JUC源码分析-线程池篇(三)ScheduledThreadPoolExecutor
JUC源码分析-线程池篇(三)ScheduledThreadPoolExecutor ScheduledThreadPoolExecutor 继承自 ThreadPoolExecutor.它主要用来在 ...
- JUC源码分析-线程池篇(三)Timer
JUC源码分析-线程池篇(三)Timer Timer 是 java.util 包提供的一个定时任务调度器,在主线程之外起一个单独的线程执行指定的计划任务,可以指定执行一次或者反复执行多次. 1. Ti ...
随机推荐
- php 表单提交大量数据发生丢失的解决方法
最近在项目中,出现一个奇怪的现象,有一个大form里面有上千个input,提交的时候,老是发现post过来的数据不完整,一开始还怀疑是html 表单名称有冲突,排除掉了.然后,网上找了一堆,php.i ...
- 11-page分页原理
创建一个分页对象PageBean<T>来存储分页信息+实体信息, 客户端请求时传递分页信息, 服务端将实体信息+分页信息放进分页对象返回给客户端. 实例如下: listStudent.js ...
- SQL语句检索数据排序及过滤
阅读目录 一:排序检索数据 二:过滤数据 三:高级数据过滤 四:用通配符进行过滤 回到顶部 一:排序检索数据 1.1 排序数据 比如查询数据库中表数据的时候,我们使用如下语句: select * fr ...
- maven eclipse web 项目 问题 cannot change version of project facet dynamic web module to 3.0
cannot change version of project facet dynamic web module to 3.0 修改 web.xml 头部 xsi:schemaLocation=&q ...
- SQLserver查询库中包含某个字段的表
select [name] from [TPMS_PRD].[dbo].sysobjects where id in(select id from [TPMS_PRD].[dbo].syscolumn ...
- Python--day03(变量、数据类型、运算符)
day02主要内容回顾 1.语言的分类 -- 机器语言:直接编写0,1指令,直接能被硬件执行 -- 汇编语言:编写助记符(与指令的对应关系),找到对应的指令直接交给硬件执行 -- 高级语言:编写人能识 ...
- day03(变量,常量,输入输出,注释,基本数据类型,运算符)
一,复习 ''' 1.语言的分类 -- 机器语言:直接编写0,1指令,直接能被硬件执行 -- 汇编语言:编写助记符(与指令的对应关系),找到对应的指令直接交给硬件执行 -- 高级语言:编写人能识别的字 ...
- 简单介绍Excel单元格行列指示的实现原理(俗称聚光灯功能)
原始出处:www.cnblogs.com/Charltsing/p/CellLight.html QQ:564955427 Excel单元格行列指示的实现原理(俗称聚光灯功能) 单元格行列指示功能在录 ...
- python的web运用
---恢复内容开始--- 对于大多数学过编程语言的人来说都知道大部分的编程语言都可以用来开发web运用,对于python来说也是可以的,不过对于web开发来说用python你可以选择 两个不同的框架, ...
- 数据标记系列——图像分割 & PolygonRNN++(二)
实践 1.export PATH=~/anaconda3/bin:$PATH 2.Anaconda3 中创建新环境 Conda create –name=labelme_polyrnn_pp pyth ...