Spring 实现事务管理有两种方式
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource">property>
bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
注意:
4. 一个类含有@Transactional注解修饰的方法,则Spring框架自动为该类创建代理对象,默认使用JDK创建代理对象。
(可以通过添加
5. 不能在protected、默认或者private的方法上使用@Transactional注解,否则无效。
@Service
public class CouponService implements ICouponService {
@Autowired
private IBookDao bookDao;
@Autowired
private IMoneyDao moneyDao;
@Autowired
private ICouponDao couponDao;
//立即购买
@Override
public boolean insert(String userId,String bookId, int count){
if(bookDao.enough(bookId, count)) {
//书籍足够
//书籍表库存递减
bookDao.update(bookId, count);
}
double price = bookDao.getPrice(bookId);
double total = price*count;
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;
}
}
自定义两个运行时异常
@Service
public class CarService implements ICarService {
@Autowired
ICouponService couponService;
//购物车购买
@Override
@Transactional(rollbackFor= {
BookException.class,MoneyException.class})
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;
}
运行测试代码
public class Test {
public static void main(String[] args){
ClassPathXmlApplicationContext application = new ClassPathXmlApplicationContext("application.xml");
//购物车购买
ICarService carService = application.getBean(ICarService.class);
String userId = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa";
Map<String,Integer> commodities = new HashMap<String,Integer>();
//两个余额只有10.00元,其中一个会报余额不足的异常
commodities.put("a2f39533-659f-42ca-af91-c688a83f6e49",1);
commodities.put("17139533-659f-42ca-af91-c688a83f2684",1);
carService.batch(userId, commodities);
application.close();
}
}
注意:如果指定的检查时异常不可以使用try-cath处理异常,需要进行上抛,否则即便@Transactional注解中指定了需要会回滚的异常,当代码出现异常的时候事务也不会回滚。
@Service
public class CarService implements ICarService {
@Autowired
private ICouponService couponService;
//购物车购买
@Override
@Transactional(readOnly=true)
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;
}
}
运行测试程序
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext application = new ClassPathXmlApplicationContext("application.xml");
//购物车购买
ICarService carService = application.getBean(CarService.class);
String userId = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa";
Map<String,Integer> commodities = new HashMap<String,Integer>();
commodities.put("a2f39533-659f-42ca-af91-c688a83f6e49",1);
commodities.put("17139533-659f-42ca-af91-c688a83f2684",1);
carService.batch(userId, commodities);
application.close();
}
}
@Service
public class CarService implements ICarService {
@Autowired
private ICouponService couponService;
//购物车购买
@Override
@Transactional(timeout=3)
public boolean batch(String userId,Map<String,Integer> commodities) {
Set<Entry<String, Integer>> set = commodities.entrySet();
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
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;
}
}
运行测试程序
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext application = new ClassPathXmlApplicationContext("application.xml");
//购物车购买
ICarService carService = application.getBean(CarService.class);
String userId = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa";
Map<String,Integer> commodities = new HashMap<String,Integer>();
commodities.put("a2f39533-659f-42ca-af91-c688a83f6e49",1);
commodities.put("17139533-659f-42ca-af91-c688a83f2684",1);
carService.batch(userId, commodities);
application.close();
}
}
默认值,如果有事务在运行,当前的方法就在这个事务内运行,否则,就启动一个新的事务,并在自己的事务内运行
@Service
public class CarService implements ICarService {
@Autowired
private ICouponService couponService;
//购物车购买
@Override
@Transactional(propagation=Propagation.REQUIRED)
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;
}
}
@Service
public class CouponService implements ICouponService {
@Autowired
private IBookDao bookDao;
@Autowired
private IMoneyDao moneyDao;
@Autowired
private ICouponDao couponDao;
//这里并没有添加@@Transactional注解
@Override
public boolean insert(String userId,String bookId, int count){
if(bookDao.enough(bookId, count)) {
//书籍足够
//书籍表库存递减
bookDao.update(bookId, count);
}
double price = bookDao.getPrice(bookId);
double total = price*count;
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;
}
}
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext application = new ClassPathXmlApplicationContext("application.xml");
//购物车购买
ICarService carService = application.getBean(CarService.class);
String userId = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa";
Map<String,Integer> commodities = new HashMap<String,Integer>();
commodities.put("a2f39533-659f-42ca-af91-c688a83f6e49",1);
commodities.put("17139533-659f-42ca-af91-c688a83f2684",1);
carService.batch(userId, commodities);
application.close();
}
}
结果
数据库中数据并没有改变
说明CarService类中batch方法添加了@Transactional注解并在该方法中调用CouponService类的insert方法,把事务的传递给了insert
当前方法必须启动新事务,并在它自己的事务内运行,如果有事务在运行,则把当前事务挂起,直到新的事务提交或者回滚才恢复执行
购物车
@Service
public class CarService implements ICarService {
@Autowired
private ICouponService couponService;
//购物车购买
@Override
@Transactional(propagation=Propagation.REQUIRED)
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;
}
}
订单
@Service
public class CouponService implements ICouponService {
@Autowired
private IBookDao bookDao;
@Autowired
private IMoneyDao moneyDao;
@Autowired
private ICouponDao couponDao;
//立即购买
@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public boolean insert(String userId,String bookId, int count){
if(bookDao.enough(bookId, count)) {
//书籍足够
//书籍表库存递减
bookDao.update(bookId, count);
}
double price = bookDao.getPrice(bookId);
double total = price*count;
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;
}
}
测试类
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext application = new ClassPathXmlApplicationContext("application.xml");
//购物车购买
ICarService carService = application.getBean(CarService.class);
String userId = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa";
Map<String,Integer> commodities = new HashMap<String,Integer>();
commodities.put("a2f39533-659f-42ca-af91-c688a83f6e49",1);
commodities.put("17139533-659f-42ca-af91-c688a83f2684",1);
carService.batch(userId, commodities);
application.close();
}
}
结果
数据库表中数据,并没有回滚
注意:当在同一个类中其他方法设@Transactional(propagation=Propagation.REQUIRES_NEW)不会开启新的事务,而是在该类的其他方法中仍然是同一个事务。
如果有事务在运行,当前的方法就在这个事务内运行,否则以非事务的方式运行
当前的方法不应该运行在事务中,如果有运行的事务,则将它挂起
当前方法不应该运行在事务中,否则将抛出异常
当前方法必须运行在事务内部,否则将抛出异常
如果有事务在运行,当前的方法在这个事务的嵌套事务内运行,否则就启动一个新的事务,并在它自己的事务内运行,此时等价于REQUIRED。
注意:对于NESTED内层事务而言,内层事务独立于外层事务,可以独立递交或者回滚,如果内层事务抛出的是运行异常,外层事务进行回滚,内层事务也会进行回滚。
注意:事务的隔离级别要得到底层数据库引擎的支持, 而不是应用程序或者框架的支持