Spring的事务处理

基本概念

  • 事务:一系列必须都成功的操作,只要有一步操作失败,所有其他的步骤将都要撤销。
  • 提交:当所有的操作步骤都被完整执行后,称该事务被提交。
  • 回滚:由于某一操作执行失败,导致所有步骤都没有被提交,则事务必须回滚,即回到事务执行前的状态。
  • 事务提交是数据源和应用程序之间的一个协议,而事务日志就是这个协议的书面记录。

特性

每个事务都有一些它们所共有的特性,叫做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),都提供了基于数据连接进行事务处理的功能。

一般流程:

  1. 获取数据源
  2. 获取数据连接
  3. 设定事务开始
  4. 执行相应操作
  5. 执行成功则提交,执行失败则回滚。
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>

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

肥羊汤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值