欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring事务 怎么做 @Transactional常用属性说明

程序员文章站 2022-07-12 13:23:00
...

1.什么是事务?

事务管理是企业级应用程序开发中必不可少的技术,用来确保数据的完整性和一致性;

2.Spring 实现事务管理有如下两种方式: 

编程式事务管理:

将事务管理代码嵌入到业务方法中来控制事务的提交和回滚,在编程式管理事务中,必须在每个事务操作中包含额外的事务管理代码。

声明式事务管理(推荐):

大多数情况下比编程式事务管理更好用,它将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理,Spring声明式事务管理建立在AOP基础之上,是一个典型的横切关注点,通过环绕增强来实现,其原理是对方法前后进行拦截,然后在目标方法开始之前创建或加入一个事务,在执行完毕之后根据执行情况提交或回滚事务。

3.声明式事务配置:

(1)jar包

Spring事务 怎么做 @Transactional常用属性说明

(2)在spring文件中配置:

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

<!-- 启用事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>

4.在类CouponService中添加    @Transactional  //开启注解

@Transactional  //开启注解
	public boolean insert(String userId, String bookId, int count) {
		
		System.out.println("--userId--:"+userId+"--bookId--:"+bookId+"--count--:"+count);
		
		if(bookDao.enough(bookId,count)) {
			                                  //判断书籍是否足够
			bookDao.update(bookId, count);   //修改书籍库存
			System.out.println("购买书籍满足库存");
		}
		
		double price = bookDao.getPrice(bookId);
		double total = count * price;
		
		System.out.println("price是"+price);
		System.out.println("total是"+total);
		
		if(moneyDao.enough(userId, total)) {//余额是否足够
			  //订单表添加数据
			Coupon coupon = new Coupon();
			coupon.setId(UUID.randomUUID().toString());
			coupon.setUserId(userId);
			coupon.setBookId(bookId);
			coupon.setTotal(total);
			couponDao.insert(coupon);
			//钱包递减
			moneyDao.update(userId,total);
		}
		return true;
	}

这里添加@Transactional意味着

余额和书籍数量要么一起成功要么一起失败!

aaa@qq.com有几个属性:

第一:如果没有配置的话:默认运行时异常,则回滚事务。(如果是检查时异常则不会回滚)

第二:如果此时是检查时异常,则不会回滚。

添加    @Transactional(rollbackFor=MoneyException.class) 会进行回滚。

如果此时CouponService类中使用try catch

try {
			if (moneyDao.enough(userId, total)) {//余额是否足够
				//订单表添加数据
				Coupon coupon = new Coupon();
				coupon.setId(UUID.randomUUID().toString());
				coupon.setUserId(userId);
				coupon.setBookId(bookId);
				coupon.setTotal(total);
				couponDao.insert(coupon);
				//钱包递减
				moneyDao.update(userId, total);
			} 
		} catch (Exception e) {
			// TODO: handle exception
		}


    即便上面添加了@Transactional(rollbackFor=MoneyException.class)事务也不会回滚。

6.如果出现异常不回滚

添加:

    @Transactional(noRollbackFor=MoneyException.class)
 

7.    @Transactional(readOnly=true)只读操作。

但是 @Transactional涉及修改数据操作,所以会报错:

Caused by: java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed

8.    @Transactional(readOnly=true,timeout=3)超时操作:

@Transactional(readOnly=true,timeout=3)
	public boolean insert(String userId, String bookId, int count) {
		
		System.out.println("--userId--:"+userId+"--bookId--:"+bookId+"--count--:"+count);
		
		if(bookDao.enough(bookId,count)) {
			                                  //判断书籍是否足够
			bookDao.update(bookId, count);   //修改书籍库存
			System.out.println("购买书籍满足库存");
		}
		
		double price = bookDao.getPrice(bookId);
		double total = count * price;
		
		System.out.println("price是"+price);
		System.out.println("total是"+total);
		
		if(moneyDao.enough(userId, total)) {//余额是否足够
			  //订单表添加数据
			Coupon coupon = new Coupon();
			coupon.setId(UUID.randomUUID().toString());
			coupon.setUserId(userId);
			coupon.setBookId(bookId);
			coupon.setTotal(total);
			couponDao.insert(coupon);
			//钱包递减
			moneyDao.update(userId,total);
		}

在该方法内,如果没有按照设定时间完成,则会回滚事务并报错:

org.springframework.transaction.TransactionTimedOutException

9.propagation:指定事务传播行为

添加新包

Spring事务 怎么做 @Transactional常用属性说明

附源码:

CarService

package com.jd.car;

import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.jd.coupon.service.CouponService;
import com.jd.coupon.service.ICouponService;

@Service
public class CarService implements ICarService {

	@Autowired
	private ICouponService CouponService;
	
	//购物车购买
	@Override
	public boolean batch(String userId,Map<String,Integer> commodities) {
		Set<Entry<String, Integer>> set = commodities.entrySet();
		for (Entry<String, Integer> commodity : set) {
			String bookId = commodity.getKey();
			int count = commodity.getValue();
			System.out.println(bookId+","+count);
			CouponService.insert(userId,bookId, count);
		}
		return true;
	}

}

ICarService

package com.jd.car;

import java.util.Map;

public interface ICarService {

	//购物车购买
	boolean batch(String userId,Map<String,Integer> commodities);
	
}

购买逻辑:

Spring事务 怎么做 @Transactional常用属性说明

首先这里购买两本书,跳转到batch

Spring事务 怎么做 @Transactional常用属性说明

然后是方法

Spring事务 怎么做 @Transactional常用属性说明

同时(下面也要同时成功,同时失败)

Spring事务 怎么做 @Transactional常用属性说明

Spring事务 怎么做 @Transactional常用属性说明

这里也是。

 

提示:再 CarService类中如果不加事务,有可能第一本书购买成功,第二本书购买失败。加上事务则:要么同时成功,要么同时失败。

所以这里在CarService类中加上@Transactional

REQUIRED(默认情况):默认情况AB是同一个事务,要么购买成功,要么购买失败。

Spring事务 怎么做 @Transactional常用属性说明

如果改为:    @Transactional(propagation=Propagation.REQUIRES_NEW)

Spring事务 怎么做 @Transactional常用属性说明

则为两个不同是事务,各自运行,仍然会出现第一个购买成功,第二个购买失败的现象。