事务有一系列操作组成,这些操作是一个整体,密不可分,也就是说这些操作要么都执行成功,要么都不会执行。
事务的CAID属性
- 原子性:一个事务是一个不可分割的最小工作单元。
- 一致性:数据库总是从一个状态转换到另一个一致性的状态。
- 隔离性:通常来说,一个事务所做的修改在最终提交之前,对其他事务是不可见的。
- 持久性:一旦事务提交,其所做的修改就会永久保存到数据库中。
事务的4种隔离级别
每一种隔离级别都规定了在一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的。较低的隔离级别通常意味着较高的并发性,系统的开销更低。
- READ UNCOMMITTED(未提交读):事务中所做的修改,即使没有提交,对其他事务也都是可见的。
- READ COMMITTED(提交读):事务中所做的修改,在没有提交之前,对其他事务是不可见的。一个事务开始时,只能“看到”已经提交的事务所做的修改。
- REPEATABLE READ(可重复读):该级别保证了在同一个事务中,多次读取同样记录的结果是一致的。但是理论上,可重复读仍无法解决“幻读”问题,幻读就是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围插入了新的记录,当之前的事务再次读取该范围内的记录时,会产生“幻行”问题。InnoDB通过多版本并发控制(MVCC)解决幻读问题。
- SERIALIZABLE(可串行化):是最高的隔离级别,通过强制事务串行化执行,避免了前面所说的幻读问题。
事务的隔离级别越高,数据库事务并发执行性能越差,能处理的操作越少。因此在实际项目开发中为了考虑并发性能一般使用提交读隔离级别,它能避免丢失更新和脏读,尽管不可重复读和幻读不能避免,但可以在可能出现的场合使用悲观锁或乐观锁来解决这些问题。
Spring事务类型
数据库事务类型有本地事务和分布式事务:
- 本地事务:即普通的事务,CAID限定于一台服务器上。
- 分布式事务:涉及两个或多个数据库源的事务,即跨越多台同类或异类数据库的事务(由每台数据库的本地事务组成的),分布式事务旨在保证这些本地事务的所有操作的ACID,使事务可以跨越多台数据库。
按是否通过编程实现事务有声明式事务和编程式事务两种:
- 声明式事务: 通过注解或XML配置文件指定事务信息;
- 编程式事务:通过编写代码实现事务。
Spring事务管理
Spring核心功能之一就是提供事务管理功能,不管使用Spring JDBC框架还是集成第三方框架使用该API进行事务编程,都提供了一致的编程式事务管理API,并且是无侵入式的事务支持。
Spring有一个事务管理器抽象,对于不同的ORM框架,通过实现策略接口PlatformTransactionManager,从而支持各种ORM的事务管理。
public interface PlatformTransactionManager {
/**
* 根据var1定义的事务类型返回一个已经激活的事务或创建一个新的事务,
* 返回的是TransactionStatus对象代表了当前事务的状态,其中该方法抛出
* TransactionException(未检查异常)表示事务由于某种原因失败。
*/
TransactionStatus getTransaction(TransactionDefinition var1) throws TransactionException;
// 提交事务
void commit(TransactionStatus var1) throws TransactionException;
// 撤销事务
void rollback(TransactionStatus var1) throws TransactionException;
}
事务定义接口TransactionDefinition:
public interface TransactionDefinition {
int PROPAGATION_REQUIRED = 0;
int PROPAGATION_SUPPORTS = 1;
int PROPAGATION_MANDATORY = 2;
int PROPAGATION_REQUIRES_NEW = 3;
int PROPAGATION_NOT_SUPPORTED = 4;
int PROPAGATION_NEVER = 5;
int PROPAGATION_NESTED = 6;
int ISOLATION_DEFAULT = -1;
int ISOLATION_READ_UNCOMMITTED = 1;
int ISOLATION_READ_COMMITTED = 2;
int ISOLATION_REPEATABLE_READ = 4;
int ISOLATION_SERIALIZABLE = 8;
int TIMEOUT_DEFAULT = -1;
// 事务的传播行为
int getPropagationBehavior();
// 事务隔离级别
int getIsolationLevel();
// 事务超时时间
int getTimeout();
boolean isReadOnly();
String getName();
}
事务状态接口TransactionStatus :
public interface TransactionStatus extends SavepointManager, Flushable {
// 是否是新事务
boolean isNewTransaction();
// 当前事务是否有保存点
boolean hasSavepoint();
// 设置事务应该回滚
void setRollbackOnly();
// 当前事务是否应该回滚
boolean isRollbackOnly();
void flush();
// 当前事务否已完成
boolean isCompleted();
}
Spring内置事务管理器实现
- DataSourceTransactionManager:位于org.springframework.jdbc.datasource包中,数据源事务管理器,提供对单个javax.sql.DataSource事务管理,用于Spring JDBC抽象框架、iBATIS或MyBatis框架的事务管理;
- JdoTransactionManager:位于org.springframework.orm.jdo包中,提供对单个javax.jdo.PersistenceManagerFactory事务管理,用于集成JDO框架时的事务管理;
- JpaTransactionManager:位于org.springframework.orm.jpa包中,提供对单个javax.persistence.EntityManagerFactory事务支持,用于集成JPA实现框架时的事务管理;
- HibernateTransactionManager:位于org.springframework.orm.hibernate3包中,提供对单个org.hibernate.SessionFactory事务支持,用于集成Hibernate框架时的事务管理;该事务管理器只支持Hibernate3+版本,且Spring3.0+版本只支持Hibernate 3.2+版本;
- JtaTransactionManager:位于org.springframework.transaction.jta包中,提供对分布式事务管理的支持,并将事务管理委托给Java EE应用服务器事务管理器;
- OC4JjtaTransactionManager:位于org.springframework.transaction.jta包中,Spring提供的对OC4J10.1.3+应用服务器事务管理器的适配器,此适配器用于对应用服务器提供的高级事务的支持;
- WebSphereUowTransactionManager:位于org.springframework.transaction.jta包中,Spring提供的对WebSphere 6.0+应用服务器事务管理器的适配器,此适配器用于对应用服务器提供的高级事务的支持;
- WebLogicJtaTransactionManager:位于org.springframework.transaction.jta包中,Spring提供的对WebLogic 8.1+应用服务器事务管理器的适配器,此适配器用于对应用服务器提供的高级事务的支持。
声明对本地事务的支持
Spring配置文件如下:
<context:component-scan base-package="com.luo.jdbc"></context:component-scan>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://192.168.1.xxx:3306/zzz"></property>
<property name="username" value="xxx"></property>
<property name="password" value="xxx"></property>
</bean>
<!--事务管理器-->
<tx:annotation-driven transaction-manager="txManager"/>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--JDBC模板-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource">
<ref bean="dataSource" />
</property>
</bean>
然后自定义事务:
@Service
public class MyTransaction1 {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private MyTransaction2 transaction2;
@Transactional(propagation = Propagation.REQUIRED)
public void test1() {
jdbcTemplate.execute("update user set name = '1' where id = 3");
try {
transaction2.test2();
} catch (Exception e) {
System.out.println("transaction2 throws exception");
}
}
}
@Service
public class MyTransaction2 {
@Autowired
private JdbcTemplate jdbcTemplate;
@Transactional(propagation = Propagation.REQUIRED)
public void test2() {
jdbcTemplate.execute("update user set name = '2' where id = 3");
// 如果这里没有抛出异常,则事务就会正常执行完毕
throw new RuntimeException();
}
}
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("springConfig.xml");
MyTransaction1 transaction1 = ctx.getBean(MyTransaction1.class);
transaction1.test1();
System.out.println("hi, spring transaction");
}
Spring事务的传播行为
事务传播是Spring事务中一个重要概念,当调用一个基于Spring的Service接口方法时,它将运行于Spring管理的事务环境中,Service接口方法可能会在内部调用其它的Service接口方法以共同完成一个完整的业务操作(比如上面代码中MyTransaction1.test1
调用MyTransaction2.test2
),因此就会产生服务接口方法嵌套调用的情况, Spring通过事务传播行为控制当前的事务如何传播到被嵌套调用的目标服务接口方法中。
Spring在TransactionDefinition接口中规定了7种类型的事务传播行为,它们规定了事务方法和事务方法发生嵌套调用时事务如何进行传播:
- PROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
- PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
- PROPAGATION_MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。
- PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
- PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
- PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
参考资料: