Spring数据访问和事务
- 1、模型
- 2、解耦
- 3、实现
- 3.1 核心接口
- 3.2 代码分析
- 3.2.1 事务管理
- 3.2.2 数据访问
- 4、使用
- 4.1 编程模式
- 4.2 配置模式
- 4.2.1 声明式配置方式
- 4.2.2 注解式配置方式
- 5、总结
1、模型
在一般的编程习惯中,Spring的数据访问和事务处理的层次结构归纳如下图所示:
图. 1
2、解耦
Spring事务作为一个独立的组件,其目的就是为了与数据访问组件进行分离,这也是Spring事务框架设计的原则。根据这一职责清晰的原则,Spring在设计时就对事务和数据访问进行了很好的职责划分,这个可以从spring-tx和spring-jdbc这两个包就可以看出来。
但是在实际的代码中,会遇到一个棘手的问题:事务和数据访问操作都需要同一个数据库连接资源,那么它们之间怎么传递呢?
这里涉及三个方面:一是线程安全,二是资源的唯一性,三是事务和数据访问的解耦。
图. 2
在图2中的1、2和3这三个地方都需要使用数据库连接,并且是同一个连接。Spring的做法是将该连接放在一个统一的地方,要使用该资源,都从这个地方获取,这样就解决了事务模块和数据访问模块之间的紧耦合。
解除耦合之后,对于不同的ORM技术,则需要提供不同的事务管理实现,如下图所示:
图. 3
3、实现
3.1 核心接口
Spring事务框架的核心接口是:TransactionDefinition,TransactionStatus和PlatformTransactionManager。
TransactionDefinition用于定义事务属性,包括事务的隔离级别,事务的传播行为,事务的超时时间和是否为只读事务。对于隔离级别和传播行为这里就不深入了,可以网上找到很多资料。事务的超时时间就是一个事务需要在规定的时间里完成。只读事务表示在一个事务里不允许进行写操作。
TransactionStatus表示整个事务处理过程中的事务状态,通过事务状态可以进行事务的相应操作。
PlatformTransactionManager是对整个事务行为的抽象,定义了一个完整事务过程中的相关操作。对于不同的ORM技术,需要有不同的实现。
3.2 代码分析
下面简单分析一下事务和数据访问之间数据库连接资源是如何传递的,以JdbcTemplate为例,代码如下:
3.2.1 事务管理
图. 4
代码1:事务管理器首先获取事务对象(具体步骤请看代码2),然后根据事务对象判断是否已经存在事务,如果已经存在事务,则根据传播特性做进一步处理,这里就不介绍了;如果不存在事务,则开启一个新事务,开启新事务的具体内容见代码3。
1 //************************************** 代码1: AbstractPlatformTransactionManager.java **************************** 2 public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException { 3 Object transaction = doGetTransaction(); //获取事务 4 ...... 5 if (isExistingTransaction(transaction)) { //如果已经存在事务,则根据传播特性再进一步处理 6 // Existing transaction found -> check propagation behavior to find out how to behave. 7 return handleExistingTransaction(definition, transaction, debugEnabled); 8 } 9 10 // No existing transaction found -> check propagation behavior to find out how to proceed. 11 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) { 12 throw new IllegalTransactionStateException( 13 "No existing transaction found for transaction marked with propagation 'mandatory'"); 14 } 15 else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED || //如果不存在事务,则开启一个新事务 16 definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW || 17 definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { 18 SuspendedResourcesHolder suspendedResources = suspend(null); 19 20 try { 21 boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); 22 DefaultTransactionStatus status = newTransactionStatus( 23 definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); 24 doBegin(transaction, definition); //开启事务 25 prepareSynchronization(status, definition); 26 return status; 27 } 28 ...... 29 } 30 ...... 31 }
代码2:获取事务对象,事务对象中包含了一个数据库连接,这个数据库连接是从事务同步管理器中获取的。事务同步管理器管理了一个ThreadLocal变量,它用于存放当前线程使用的数据连接资源。
代码3:开启事务,从事务对象中获取连接,如果连接不存在则从数据库连接池中获取新的连接,关闭自动提交,设定事务超时时间,最后将数据库连接存储在事务同步管理中,即绑定在当前线程上。
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="meilvDataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
在类或者方法上使用注解@Transactional就可以进行事务管理了。
5、总结
Spring的事务管理和数据访问模块职责相当清晰,认识这一设计原则对我们学习他们具有根本性的作用,本文试图理清两者之间的关系,而这所谓的“关系”就是数据库连接,这也是Spring事务和数据访问都需要依赖的基础。
从另一方面来看,Spring对这两者关系的处理很值得我们学习和借鉴,不管是多线程编程的线程安全性,还是模块之间的解耦。