Spring学习(10)Spring应用之事务

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/SDDDLLL/article/details/86630783

事务管理是AOP的应用。在Spring中使用三种方法,来实现对事务的管理:

  1. 使用事务代理工厂管理事务
  2. 使用事务注解管理事务
  3. 使用AspectJ的AOP配置管理事务

一、Spring事务管理API

Spring的事务管理,主要用到两个事务相关的接口。

(1)事务管理器接口

事务管理器是PlatformTransactionManager接口对象。其主要完成事物的提交、回滚,以及事务的状态信息。

看一下其官方API:

A:常用的两个实现类:

  • DataSourceTransactionManager:使用JDBC或者是iBatis进行持久化数据时使用。
  • HibernateTransactionManager:使用Hibernate进行持久化数据时候使用

B:Spring的回滚方式

Spring事物的默认回滚方式是:运行时异常发生回滚,受查时异常发生提交。受查时异常也可以手工设置回滚。

什么是运行时异常和受查时异常?

(2)事务管理接口:

事务定义接口TransactionDefination中定义了事物描述相关的三类常量:事务隔离级别、事务传播行为、事务默认超时时限。

A:事务隔离级别:

Spring定义了五个事务隔离界别:

  1. DEFAULT:采用DB默认的事务隔离界别。
  2. READ_UNCOMMITTED:读未提交。未解决任何并发问题。
  3. READ_COMMITTED:读已提交。解决脏读,存在不可重复读和幻读。
  4. REPEATABLE_READ:可重复读。解决脏读、不可重复读。存在幻读
  5. SERIALIZABLE:串行化。不存在并发问题。

B:事务传播行为常量

事务传播行为指的是:处于不同事物中的方法在相互调用时候,执行期间事务的维护情况。比如说A事务中的方法doSome调用B事务中的方法doOther,在调用执行期间,事务的维护情况,这就叫做事务传播行为。

这里有七个事务传播行为常量:

  1. REQUIRED:指定的方法必须在事务内执行
  2. SUPPORTS:指定的方法支持当前事务,但是若没有当前事务,也可以以非事务方式执行。
  3. MANDATORY:指定的方法必须在当前事务内执行,若没有当前事务,则直接抛出异常。
  4. REQUIRES_NEW:总是新建一个事务,若当前存在事务,则挂起,直到新事务执行完毕。
  5. NOT_SUPPORTS:指定的方法不能再事务环境中执行,若当前存在事务,就将当前事务挂起
  6. NEVER:指定的方法不能在事务环境中执行,若当前存在事务,就直接抛出异常
  7. NESTED:指定的方法2必须在事务内执行,若当前存在事务,则直接嵌套事务内执行:若没有当前事务,则创建一个新事务。

C:默认事务超时时限

上面的这些概念太抽象了,先知道,然后直接看例子。

现在有一个需求,那就是买股票的例子。现在存在两个实体Account(银行账户)和Stock(股票账户)。买完了股票,银行账户应该扣除相应的金额。股票账户应该增加相应的股票。

二、环境搭建、

1、创建数据库和表

2、实体类

首先是Account:

public class Account {
	private Integer aid;
	private String aname;
	private double balance;  // 余额
	//有参构造方法和无惨构造方法
    
}

然后是股票类Stock

public class Stock {
	private Integer sid;
	private String sname;   // 股票名称
	private int count;      // 股票数量
	//get和set方法
    //toString方法
    //有参和无参构造器
}

3、定义接口

public interface IAccountDao {
	void insertAccount(String aname, double money);
	void updateAccount(String aname, double money, boolean isBuy);
}

还有股票接口

public interface IStockDao {
	void insertStock(String sname, int amount);
	void updateStock(String sname, int count, boolean isBuy);
}

4、定义接口实现类

public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {

	@Override
	public void insertAccount(String aname, double money) {
		String sql="insert into account(aname,balance) values(?,?)";
		this.getJdbcTemplate().update(sql, aname,money);

	}

	@Override
	public void updateAccount(String aname, double money, boolean isBuy) {
		String sql=null;
		if(isBuy){
			sql="update account set balance=balance-? where aname=? ";
		}else{
			sql="update account set balance=balance+? where aname=? ";
		}
		this.getJdbcTemplate().update(sql,money,aname);
	}

}

另一个实现类:

public class StockDaoImpl extends JdbcDaoSupport implements IStockDao {

	@Override
	public void insertStock(String sname, int amount) {
		String sql="insert into stock(sname,count) values(?,?)";
		this.getJdbcTemplate().update(sql, sname,amount);

	}

	@Override
	public void updateStock(String sname, int count, boolean isBuy) {
		String sql="update stock set count=count-? where sname=?";
		if(isBuy){
			sql="update stock set count=count+? where sname=?";
		}
		this.getJdbcTemplate().update(sql,count,sname);
	}

}

5、定义Service接口

public interface IBuyStockService {

	void openAccount(String aname,double money);
	void openStock(String sname,int amount);
	void buyStock(String aname,double money,String sname,int count);
}

6、定义Service接口的实现类

public class BuyStockServiceImpl implements IBuyStockService {

	private IAccountDao adao;
	private IStockDao sdao;
	
	public void setAdao(IAccountDao adao) {
		this.adao = adao;
	}

	public void setSdao(IStockDao sdao) {
		this.sdao = sdao;
	}

	@Override
	public void openAccount(String aname, double money) {
		adao.insertAccount(aname,money);

	}

	@Override
	public void openStock(String sname, int amount) {
		sdao.insertStock(sname,amount);

	}

	@Override
	public void buyStock(String aname, double money, String sname, int count) {
		boolean isBuy=true;
		adao.updateAccount(aname,money,isBuy);
		sdao.updateStock(sname,count,isBuy);

	}

}

7、配置文件

<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" xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd">

	<!-- 注册数据源:C3P0 -->
	<bean id="myDateSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driver}"/>
		<property name="jdbcUrl" value="${jdbc.url}"/>
		<property name="user" value="${jdbc.user}"/>
		<property name="password" value="${jdbc.password}"/>
	</bean>
	
	<!-- 注册数据库属性文件:方式二 -->
	<context:property-placeholder location="classpath:jdbc.properties"/>
	
	
	<!-- 注册账户dao -->
   <bean id="accountDao" class="com.fdd.dao.AccountDaoImpl"  >
   		<property name="dataSource" ref="myDateSource"></property>
   </bean>
	<!-- 注册股票dao -->
   <bean id="stockDao" class="com.fdd.dao.StockDaoImpl"  >
   		<property name="dataSource" ref="myDateSource"></property>
   </bean>
	
	<!-- 注册service -->
	<bean id="buyStockService" class="com.fdd.service.BuyStockServiceImpl">
		<property name="adao" ref="accountDao"></property>
		<property name="sdao" ref="stockDao"></property>
	</bean>
</beans>

8、测试

public class MyTest {
	private IBuyStockService service;
	@Before
	public void before(){
		String resource = "applicationContext.xml";
		ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
		service =(IBuyStockService) ac.getBean("buyStockService");
	}
	
	@Test
	public void test01(){
		service.openAccount("张三", 10000);
		service.openStock("西工大", 0);
	}
	
	@Test
	public void test02(){
		service.buyStock("张三", 2000, "西工大", 5);
	}

}

看结果:

首先我们在test01中在张三的账户上存了1万,然后再西工大的开了户

然后再test02中张三花了2000,买了西工大5只股票。此时Account应该还有8000,然后Stock有5只股票。

三、使用Spring的事务代理工厂管理事务

在上面的例子中,我们使用的是Spring的事务管理机制。下面使用事务代理。事务代理使用的是TransactionProxyFactoryBean,这个类需要初始化一些属性:

  • transactionManager:事务管理器
  • target:目标对象,也就是service的实现类
  • transactionAttributes:事务属性设置

在配置代理的时候,对于受查异常的回滚方式:

  • -异常方式:但发生指定的异常时候事务回滚
  • +异常方式:当发生指定的异常时事务提交

下面看看使用代理工厂如何实现事务的管理:

1、导入jar

这里需要导入两个jar

  1. com.springsource.org.aopaliance-1.0.0.jar
  2. spring-aop-4.2.1.RELEASE.jar

2、添加事务管理器DataSourceTransactionManager

3、添加事务代理TransactionProxyFactoryBean

4、测试

四、使用Spring的事务注解管理事务

通过@Transactional注解方式,也可以将事务织入到相应方法中。而事务注解方式,只需要添加一个tx标签,告诉Spring使用注解来完成事务的织入。

<tx:annotion-driven transaction-maneger="myTransactionManager"/>

这里只列出了一个属性,还有其他的一些属性:

  • propagation:用于设置事务传播属性。该类型是枚举类型,比如Propagation.REQUIRED
  • isolation:用于设置事务的隔离级别
  • readOnly:用于设置该方法对数据库的操作是否是只读的。
  • timeout:用于设置本操作与数据库连接的超时时限
  • rollbackFor:指定需要回滚的异常类
  • rollbackForClassName:指定需要回滚的异常类类名
  • noRollbackForClassName:指定不需要回滚的异常类类名

1、在容器中添加事务管理器

2、在Service的实现类中添加注解:

3、修改配置文件

4、修改测试类

五、使用AspectJ的AOP配置管理事务(重要)

前面说了一大堆,发现,各种配置,代码的修改。不方便。并且每一个目标类都需要配置目标代理。在这里使用AspectJ来管理事务。直接上代码看吧

1、导入jar

在这里需要四个jar:

  • 两个前面使用注解导入的jar
  • com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
  • spring-aspects-4.2.1.RELEASE

2、添加事务管理

3、配置事务通知

为事务通知设置相关属性,指定要将事务以什么方式植入给那些方法。比如在之前的买股票的例子,再买股票的时候如果发生了异常,这时候就需要事务回滚。在这里StockException是自定义的股票异常

4、配置顾问

猜你喜欢

转载自blog.csdn.net/SDDDLLL/article/details/86630783