Spring入门5——事务管理

事务:

事务是访问数据库的一个操作序列,数据库应用系统通过事务集来完成对数据库的存取。

事务具有四个特性:

原子性(atomicity), 一致性(consistency), 隔离性(isolation)和持久性(durability)。


 原子性 即不可分割性,事务要么全部被执行,要么就全部不被执行。

一致性 事务的执行使得数据库从一种正确状态转换成另一种正确状态。即一个事务内有一个操作失败时,所有的更改过的数据都必须回滚到修改前的状态。

隔离性 事务提交前,事务对数据的修改对事务不可见。

持久性 事务正确提交后,其结果将永久保存在数据库中,即使在事务提交后有了其他故障,事务的处理结果也会得到保存。


而Spring为事务管理提供了三个接口来帮助我们管理事务。

1.PlatformTransactionManager接口

2.TransactionDefinition接口

3.TransactionStatus接口


这三个接口的关系如下:


很显然,我们是首先通过TransactionDifinition接口来定义事务的行为和参数等等,然后交给PlatformTransactionManager来管理使用,而我们可以通过TransactionStatus接口来查看当前事务的状态。


PlatformTransactionManager接口:


这是Spring官方文档给的解释,可以看到正如上面的关系图中所示,PlatformTransactionManager接口有很多实现类,意思就是根据你的持久层框架来选择提供不同的实现类。例如使用JDBC的框架,那么就要使用DataSourceTransactionManager这一个实现类了。


TransactionDefinition接口:

这个接口里面定义了许多常量,有点多,就不一一列举了,这里给个连接:TransactionDefinition接口官方文档

主要分为三个部分:

1.隔离级别 2.传播行为3.事务超时


隔离级别

指若干个并发的事务之间的隔离程度。

对于隔离级别,TransactionDefinition定义了以下常量:

1.ISOLATION_READ_UNCOMMITTED: 该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读,不可重复读和幻读。

2.ISOLATION_READ_COMMITTED: 该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,但不能方式不可重复读和幻读。

3.ISOLATION_REPEATABLE_READ: 该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。该级别可以防止脏读和不可重复读,但不可防止幻读。

4.ISOLATION_SERIALIZABLE: 所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

5.ISOLATION_DEFAULT: 这是默认值,表示使用底层数据库的默认隔离级别。例如MySQL使用ISOLATION_REPEATABLE_READ的隔离级别。


传播行为

指在开始当前事务之前,当前事务方式执行行为的选择。

对于传播行为,TransactionDefinition定义了以下常量:

1.PROPAGATION_REQUIRED: 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。

3.PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

6.PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。

2.PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。

4.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。

5.PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。

7.PROPAGATION_NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行。


前三个都是要求新事务加入当前事务,而后三个都是严格保证不在同一个事务当中。


事务超时
指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。


Spring支持两种方式的事务管理

1.编程式事务管理(在实际应用中很少使用)

2.声明式事务管理(通过AOP实现,一般使用方法)


在事务管理之前,首先我们要连接数据库。

需要如下maven依赖:

<dependency>
	<groupId>junit</groupId>
	<artifactId>junit</artifactId>
	<version>4.11</version>
</dependency>
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>5.1.26</version>
</dependency>
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>druid</artifactId>
	<version>0.2.25</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context</artifactId>
	<version>3.2.4.RELEASE</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-jdbc</artifactId>
	<version>3.2.4.RELEASE</version>
</dependency>

我们有一个Service类,一个Service实现类ServiceImpl。一个DAO类,DAO实现类DAOImpl。数据库持久采用JDBC的方式。

public interface Service {
	public void transfer(String from, String to, double money);
}
public class ServiceImpl implements Service {

	private DAO dAO;

	public void setdAO(DAO dAO) {
		this.dAO = dAO;
	}

	public void transfer(String from, String to, double money) {
		dAO.delete(from, money);
		dAO.add(to, money);
	}

}
public interface DAO {
	public void delete(String from, double money);
	public void add(String to, double money);
}
public class DAOImpl extends JdbcDaoSupport implements DAO {

	public void delete(String from, double money) {
		String sql = "update account set money = money - ? where name = ?";
		this.getJdbcTemplate().update(sql, money, from);
	}

	public void add(String to, double money) {
		String sql = "update account set money = money + ? where name = ?";
		this.getJdbcTemplate().update(sql, money, to);
	}

}
applicationContext文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

	<context:property-placeholder location="classpath:jdbc.properties"/>

	<!-- 数据库连接池 -->
	<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
		<property name="url" value="jdbc:mysql://localhost:3306/test" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean>

	<!-- 获取jdbc模版 -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource" />
	</bean>
	
	<!-- 配置业务类 -->
	<bean id = "service" class = "spring.transactionDemo.Service.ServiceImpl">
		<property name="DAO" ref = "DAO"></property>
	</bean>
	
	<!-- 配置DAO类 -->
	<bean id = "DAO" class = "spring.transactionDemo.DAO.DAOImpl">
		<property name="jdbcTemplate" ref="jdbcTemplate" />
	</bean>

</beans>

Junit测试:

public class AppTest {

	@Test
	public void Test() {
		try {
			ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:applicationContext-transactionCode.xml");
			Service service = (Service)ctx.getBean("service");
			service.transfer("a", "b", 10);
			ctx.destroy();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}
结果如下:


在上面可以看到,在Service中,一个业务需要两个数据库的操作来完成。分成delete()和add()。

如果在执行完delete()之后,没执行add()之前,突然出现了某些异常,导致中断,会出现什么问题呢?

比如:将Service中的业务进行如下修改:

public void transfer(String from, String to, double money) {
	dAO.delete(from, money);
	int a = 1/0;
	dAO.add(to, money);
}
会发生什么事呢?

我们就发现,a的钱被扣除了,但是b的钱并没有增加。这就是需要进行事务管理的一个例子。


编程式事务管理

1.配置事务管理器transactionManager。

2.配置事务管理模版TransactionTemplate。

3.在需要使用事务管理的地方增加事务管理。


在applicationContext文件中增加如下配置:

<!-- 配置事务管理器 -->
<bean id = "transactionManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref = "dataSource"></property>
</bean>
	
<!-- 配置事务管理模版,可以简化事务管理 -->
<bean id = "transactionTemplate" class = "	org.springframework.transaction.support.TransactionTemplate">
	<property name="transactionManager" ref = "transactionManager"></property>
</bean>
因为Service类中需要增加事务,所以修改Service类:

public class ServiceImpl implements Service {

	private DAO dAO;
	
	private TransactionTemplate transactionTemplate;

	public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
		this.transactionTemplate = transactionTemplate;
	}

	public void setdAO(DAO dAO) {
		this.dAO = dAO;
	}

	public void transfer(final String from, final String to, final double money) {
		transactionTemplate.execute(new TransactionCallbackWithoutResult() {
			@Override
			protected void doInTransactionWithoutResult(TransactionStatus status) {
				dAO.delete(from, money);
				int a = 1/0;
				dAO.add(to, money);
			}
		});
	}

}
这里需要自行增加transactionTemplate的依赖。使用template的execute方法,即可管理事务。

结果如下:



结果并不会改变,也就是说发生了rollback。我们的配置是成功的。



声明式事务管理
一:传统的方法(使用TransactionProxyFactoryBean代理的方式)

方法:

1.配置TransactionManager

2.配置需要的业务层代理

如下:

<!-- 配置事务管理器 -->
<bean id="transactionManager"
  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 配置代理 -->
<bean id="serviceProxy" class=" org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
  <property name="target" ref="service"></property>
  <property name="transactionManager" ref="transactionManager"></property>
  <property name="transactionAttributes">
    <props>
      <prop key="*">PROPAGATION_REQUIRED</prop>
    </props>
  </property>
</bean>
其中,prop中配置就是前面说过的TransactionDefinition。可以参考TransactionProxyFactoryBean源文件里的示例进行配置。

这里需要注意的就是,我们获取bean的时候,需要使用代理bean,而不是直接使用原本的bean,这非常好理解,基于AOP的思想嘛。


二.基于AspectJ的方式。

方法:

1.配置TransactionManager

2.配置事物通知。

3.配置切面


如下:

<!-- 配置事务管理器 -->
<bean id="transactionManager"
  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 配置事务通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
  <tx:attributes>
    <tx:method name="*" propagation="REQUIRED"/>
  </tx:attributes>
</tx:advice>

<!-- 配置切面 -->
<aop:config>
  <aop:pointcut expression="execution(* spring.transactionDemo.Service.*.*(..))" id="pointcut1"/>
  <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
</aop:config>
在通知里面也可以设置TransactionDefinition。

这个方法需要aspectJ wearver包的依赖。


三.基于注解的方式。

方法:

1.配置TransactionManager

2.增加<tx:annotation-driven transaction-manager="transactionManager"/>标签

3.在需要使用的类上增加@Transactional注解即可。


需要的TransactionDefinition可以在@Transational的属性中进行设置。

如下:

<!-- 配置事务管理器 -->
<bean id="transactionManager"
  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"></property>
</bean>

<tx:annotation-driven transaction-manager="transactionManager"/>
只需要用@Transactional标签标注Service类就可以了。

使用注解的方式非常的简单!!!



总结:

对于前两种方法,我们用的并不多,几乎很少使用。

而对于后两种方法,都是常用的方法。

基于AspectJ的方式非常灵活并且配置并不复杂,并且所有都是基于XML,一目了然。

基于注解的方式非常的简单,适用于中小型的项目,几乎不怎么需要配置,就可以完成事务的管理了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值