基本概念
- 事务:一系列必须都成功的操作,只要有一步操作失败,所有其他的步骤将都要撤销。
- 提交:当所有的操作步骤都被完整执行后,称该事务被提交。
- 回滚:由于某一操作执行失败,导致所有步骤都没有被提交,则事务必须回滚,即回到事务执行前的状态。
- 事务提交是数据源和应用程序之间的一个协议,而事务日志就是这个协议的书面记录。
特性
每个事务都有一些它们所共有的特性,叫做ACID特性。即原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。
- (1)原子性(Atomicity):把事务作为一个工作单应处理,所有操作都完成才算完成。如果一个操作失败,则所有步骤都失败,系统将回滚到事务开始前的状态。
- (2)一致性(Consistency):事务开始时系统处于一致状态,事务结束时系统也应该处于一致状态,不管事务成功还是失败。一致性由同组业务规则或完整性限制确定,需要事务与开发人员共同合作。
- (3)隔离性(Isolation):保证事务访问的任何数据不会受到其他事务所做的任何改变的影响,直到该事务完成。
- (4)持久性(Durability):事务执行成功,在系统中产生的结果应该是持久的。
事务处理应该保证事务的原子性、隔离性和持久性,开发人员应保证事务的一致性,保证业务规则、指定主键、设定关联和其他一些规则。当提交事务的时候,数据库根据这些设定验证其数据的一致性。如果发现事务结果与系统确定的规则一致,则事务提交;如果结果不符合要求,则事务撤销。
事务处理方式
主要有3种:关系型数据库的事务处理、传统的JDBC事务处理、分布式事务处理。
(1)关系型数据库的事务处理
关系型数据库提供了事务处理的能力。
在数据库中,调用事务处理的示例代码:
Begin Transaction(启动事务处理) //整个事务处理的起始位置
//事务处理步骤
Commit或RollBack(提交或回滚) //中间的所有命令在执行到End Transaction时才会被一并执行
End Transaction(提交事务处理) //整个事务处理的提交位置
Isolation Level 表示事务之间的隔离程度:
- read uncommitted:允许读取任何提交或未提交的记录,即不管记录是否提交都允许读取。
- read committed:只允许读取已经提交的记录。(大多数数据库默认的隔离级别)
- repeatable read:只能看到事务开始时刻的数据库的一个快照,事务开始后更改的所有记录不管提交未提交都无法看到。
(2)传统的JDBC事务处理
常用的数据库接口(JDBC、ADO),都提供了基于数据连接进行事务处理的功能。
一般流程:
- 获取数据源
- 获取数据连接
- 设定事务开始
- 执行相应操作
- 执行成功则提交,执行失败则回滚。
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;
public class HelloWorld {
private DataSource dataSource;
//获取数据源
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
Connection conn = null;
Statement stml = null;
{
try {
//获取数据连接
conn = dataSource.getConnection();
//开始启动事务
conn.setAutoCommit(false);
stml = conn.createStatement();
//执行相应操作
stml.executeUpdate("insert into hello values(1,'gf','HelloWorld')");
//执行成功则提交事务
conn.commit();
} catch (SQLException e) {
if (conn != null) {
try {
//执行不成功,则回滚
conn.rollback();
} catch (SQLException ex) {
System.out.println("数据连接有异常" + ex);
}
}
} finally {
//假如stmt不为空,则关闭 stmt
if (stml != null) {
try {
stml.close();
} catch (SQLException ex) {
System.out.println("执行操作有异常" + ex);
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException ex) {
System.out.println("数据连接有异常" + ex);
}
}
}
}
}
传统的JDBC事务处理,代码繁琐。
(3)分布式事务处理
分布式事务处理支持对多个数据库进行事务操作。
分布式事务:就是事务分布在多个资源上、由多个组件共享的事务。
特征:
- 组件要在同一原子操作中与多个资源通信。
- 多个组件要在同一原子操作中操作。
- 分布式事务需要多个不同的事务管理器的合作。
Spring的事务处理
- Spring中的事务处理实际上是基于动态AOP机制实现。
- Spring事务的中心接口是 org.springframework.transaction.PlatformtransactionManager。
- PlatformtransactionManager中有很多实现类,通常在Spring的配置文件中声明。如:TransactionManager。
PlatformtransactionManager代码如下:
public interface PlatformTransactionManager {
//目前事务
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
//提交事务
void commit(TransactionStatus status) throws TransactionException;
//事务回滚
void rollback(TransactionStatus status) throws TransactionException;
}
TransactionDefinition代表事务处理时的一些属性定义:
public interface TransactionDefinition {
//获得事务的传播行为
int getPropagationBehavior();
//获得事务的隔离层次
int getIsolationLevel();
//判断事务是否超时
int getTimeout();
//判断是否为只读事务
boolean isReadOnly();
//返回一个事务的名字
String getName();
}
TransactionStatus代表了目前的事务,通常不直接使用它,可以借助它的setRollbackOnly()方法来设定只读事务。
public interface TransactionStatus {
//判断是否是一个新事务
boolean isNewTransaction();
//设定为只读事务
void setRollbackOnly();
//判断是否为只读事务
boolean isRollbackOnly();
//判断一个事务是否完成
boolean isCompleted();
}
Spring提供了编程式事务处理(programmatic transaction management)、声明式事务处理(declarative transaction management)。
(1)编程式事务处理
- Spring提供的TransactionTemplate能够以编程的方式实现事务控制,通过实现TransactionCallback接口来使用。
- TransactionTemplate是无状态且线程安全的。
- 创建TransactionTemplate实例需要提供一个PlatformtransactionManager实例。
- 使用TransactionTemplate,只需要调用execute()方法即可。
- 要实现TransactionCallback接口,并且在 doInTransaction()方法里进行各种数据库操作。
- 如果操作成功,则 this.transactionManager.commit(status)。
- 如果有异常,则相应的进行事务回滚 rollbackOnRxception(status,ex)。
TransactionTemplate的代码:
public class TransactionTemplate extends DefaultTransactionDefinition implements InitializingBean {
private PlatformTransactionManager transactionManager = null;
//通过依赖注入
public TransactionTemplate(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public PlatformTransactionManager getTransactionManager() {
return transactionManager;
}
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
//执行完毕后调用
public void afterPropertiesSet() {
if (this.transactionManager == null) {
throw new IllegalAnnotationException("transactionManager is required");
}
}
//在这里进行事务处理
public Object execute(TransactionCallback action) throws TransactionException {
TransactionStatus status = this.transactionManager.getTransaction(this);
Object result = null;
try {
//执行具体的方法
result = action.doInTransaction(status);
} catch (RuntimeException ex) {
// Transactional code threw application exception -> roolback
rollbackOnException(status,ex);
throw ex;
} catch (Error err) {
// Transactional code threw error -> roolback
rollbackOnException(status,err);
throw err;
}
this.transactionManager.commit(status);
return result;
}
//如果有异常就rollback
private void rollbackOnException(TransactionStatus status, Throwable ex) throws TransactionException{
try {
this.transactionManager.rollback(status);
} catch (RuntimeException ex2) {
throw ex2;
} catch (Error err) {
throw err;
}
}
}
示例:
- 在HelloDAO类的create()方法里使用 TransactionTemplate来进行事务处理。
- HelloDAO中的DataSource和TransactionManager采用注入的方式实现,设置Spring配置文档 xml。这里使用的PlatformTransactionManager的实现类为 DataSourceTransactionManager。
public class HelloDAO {
private DataSource dataSource;
private PlatformTransactionManager transactionManager;
//通过依赖注入来完成管理
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
//使用TransactionTemplate对create()方法进行事务管理
public int create(String msg) {
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
//调用TransactionTemplate的execute()方法,并覆写TransactionCallback类的doInTransaction()方法,在该方法里进行对数据库的新增操作
Object result = transactionTemplate.execute(
new TransactionCallback() {
@Override
public Object doInTransaction(TransactionStatus status) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
Object resultObject = jdbcTemplate.update("INSERT INTO hello VALUES(1,'gf','HelloWorld')");
return resultObject;
}
});
}
}
<?xml version="1.0" encoding="UTF8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!--设定dataSource-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!--使用SQL Server数据集-->
<property name="driverClassName">
<value>com.microsoft.jdbc.sqlserver.SQLServerDriver</value>
</property>
<!--设定URL-->
<property name="url">
<value>jdbc:microsoft:sqlserver://localhost:1433/stdb</value>
</property>
<!--设定用户名-->
<property name="name">
<value>admin</value>
</property>
<!--设定密码-->
<property name="msg">
<value>admin</value>
</property>
</bean>
<!--设定transactionManager-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
<!--示例中的一个DAO-->
<bean id="helloDAO" class="com.gc.action.HelloDAO">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
</bean>
</beans>
当逻辑程序调用HelloDAO的create()方法往数据库新增一笔记录时,Spring会自动对这个操作使用事务处理,而不用再写rollback和commit的代码了。
当然,如果想自行重写rollback和commit也可以,只需要修改HelloDAO,不需要修改配置文件。
public class HelloDAO {
private DataSource dataSource;
private PlatformTransactionManager transactionManager;
//通过依赖注入来完成管理
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
//往数据库表hello里新增一笔数据
public void create(String msg) {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();//默认事务定义
TransactionStatus status = transactionManager.getTransaction(def);//声明事务开始
try {
//使用JdbcTemplate往数据库里新增数据
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.update("INSERT INTO hello VALUES(1,'gf','HelloWorld')");
} catch (DataAccessException ex) {
// 也可以执行status.setRollbackOnly();
transactionManager.rollback(status);
throw ex;
} finally {
transactionManager.commit(status);
}
}
}
TransactionCallback还有另外一个实现类TransactionCallbackWithoutResult,这个实现类定义了 doInTransactionWithoutResult()方法,该方法没有返回值。
public class HelloDAO {
private DataSource dataSource;
private PlatformTransactionManager transactionManager;
//通过依赖注入来完成管理
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
//使用回调的方式进行事务处理
public void create(String msg) {
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
public void doInTransactionWithoutResult(TransactionStatus status) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.update("INSERT INTO hello VALUES(1,'gf','HelloWorld')");
}
});
}
}
(2)声明式事务处理
- Spring的事务处理需要借助AOP实现。
- 对于大型软件系统,事务操作较多,使用声明式事务处理则比较好/而且它使得事务处理与具体业务逻辑分离。
示例:
- 需要一个PlatformtransactionManager的实现类,这里采用DataSourceTransactionManager类。
- 使用Spring提供的TransactionProxyFactoryBean类来实现事务代理。
- 首先在HelloDAO类的create()方法里使用IdbcTemplate往数据库新增一笔数据。
- 然后使用Spring配置文档来配置事务。
public class HelloDAO {
private DataSource dataSource;
private JdbcTemplate jdbcTemplate;
//通过依赖注入来完成管理
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
jdbcTemplate = new JdbcTemplate(dataSource);
}
//注意这里看不到事务处理的代码,一切都在配置文件中
public void create(String msg) {
jdbcTemplate.update("INSERT INTO hello VALUES(1,'gf','HelloWorld')");
}
}
<?xml version="1.0" encoding="UTF8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!--设定dataSource-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!--使用SQL Server数据集-->
<property name="driverClassName">
<value>com.microsoft.jdbc.sqlserver.SQLServerDriver</value>
</property>
<!--设定URL-->
<property name="url">
<value>jdbc:microsoft:sqlserver://localhost:1433/stdb</value>
</property>
<!--设定用户名-->
<property name="name">
<value>admin</value>
</property>
<!--设定密码-->
<property name="msg">
<value>admin</value>
</property>
</bean>
<!--设定transactionManager-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
<!--示例中的一个DAO-->
<bean id="helloDAO" class="com.gc.action.HelloDAO">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
<!--声明式事务代理-->
<bean id="helloDAOProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
<property name="target">
<ref bean="helloDAO"/>
</property>
<!--如果当前没有事务,就新建一个事务-->
<property name="transactionAttributes">
<props>
<prop key="create*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
</beans>
<prop key="create*">PROPAGATION_REQUIRED</prop>,表示对HelloDAO类中的create()方法进行事务管理,并指明当前没有事务,就新建一个事务。
- PROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务;
- PROPAGATION_SUPPORTS:如果当前没有事务,就以非事务方式执行;
- PROPAGATION_MANDATORY:如果当前没有事务,就抛出异常;
- PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起;
- PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起;
- PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
另一种配置方式:该配置与上面配置的实现结果是一样的,只是这种方式更加灵活。
<?xml version="1.0" encoding="UTF8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!--设定dataSource-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!--使用SQL Server数据集-->
<property name="driverClassName">
<value>com.microsoft.jdbc.sqlserver.SQLServerDriver</value>
</property>
<!--设定URL-->
<property name="url">
<value>jdbc:microsoft:sqlserver://localhost:1433/stdb</value>
</property>
<!--设定用户名-->
<property name="name">
<value>admin</value>
</property>
<!--设定密码-->
<property name="msg">
<value>admin</value>
</property>
</bean>
<!--设定transactionManager-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
<!--示例中的一个DAO-->
<bean id="helloDAO" class="com.gc.action.HelloDAO">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
<!--声明式事务代理-->
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
<!--如果当前没有事务,就新建一个事务-->
<property name="transactionAttributeSource">
<value>
com.gc.action.HelloDAO.create*=PROPAGATION_REQUIRED
</value>
</property>
</bean>
<bean id="helloDAOProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="interceptorNames">
<value>transactionInterceptor,helloDAO</value>
</property>
</bean>
</beans>