我们知道关系型数据库具有事务和事务隔离级别的概念,我们的Java程序一般都是通过JDBC操作数据库,比如开启事务、提交事务、设置隔离级别等,在JDBC接口中也有这些概念的定义。如果直接使用JDBC操作数据库,那么我们就需要调用相关的API,手动处理这些操作。
在Spring框架中,提供了事务管理器TransactionManager
功能。我们在配置文件中配置好事务管理器,当ORM框架从数据源中获取JDBC的Connection
对象并进行一些操作时,Spring会自动通过AOP拦截管理事务操作,这样能大大简化我们代码的编写。
事务隔离级别:关系型数据库的事务具有隔离级别这一概念,JDBC作为一个通用的抽象接口,也定义了相关隔离级别的常量值,这部分源码可以在java.sql.Connection
类中找到。
事务隔离级别 | 说明 |
---|---|
TRANSACTION_NONE | 代表JDBC驱动不支持事务,一般不会用到该项。 |
TRANSACTION_READ_UNCOMMITTED | 允许脏读,不可重复读和幻读。 |
TRANSACTION_READ_COMMITTED | 不可脏读,但允许不可重复读和幻读。 |
TRANSACTION_REPEATABLE_READ | 不可脏读和不可重复读,但允许幻读。 |
TRANSACTION_SERIALIZABLE | 不可脏读,不可重复读,幻读。 |
注:MySQL数据库默认的事务隔离级别为REPEATABLE_READ
,但JDBC驱动中默认设置了READ_COMMITTED
,大多数数据库也是此隔离级别,这是历史原因造成的。
事务传播属性:Spring框架中的事务管理器支持该配置,用于在多个声明了事务的方法互相调用时,事务如何在方法间传播。
事务传播属性 | 说明 |
---|---|
PROPAGATION_REQUIRED | 支持当前事务,如果当前没有事务就新建,最常用,默认。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 支持当前事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。 |
下面例子来自MyBatis相关章节,其中定义了一个默认的事务管理器,并配置了声明式事务@Transactional
注解的扫描。Hibernate等框架也是类似,这里仅仅是以MyBatis为例。
<!-- 配置事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 声明式事务注解扫描 -->
<tx:annotation-driven/>
<!-- 配置MyBatis的SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath*:mapper/*Mapper.xml"/>
</bean>
<!-- 配置MyBatis的Mapper扫描 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.gacfox.**.mapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
我们可以看到,事务管理器对象引用了数据源对象dataSource
,事务管理器的相关功能就会在该数据源上生效。
Spring中最常用的就是声明式事务,用法如下:
@Service("testService")
@Transactional(rollbackFor = Exception.class)
public class TestServiceImpl implements TestService {
// ... 具体实现相关代码
}
代码中,我们使用了@Transactional
标注了一个类,这标明其方法执行时,默认就会以声明的事务隔离级别和传播属性执行。rollbackFor = Exception.class
指定抛出任何异常时,即回滚事务。