BookDao.java
@Repository
public class BookDao {
@Autowired
JdbcTemplate jdbcTemplate;
/**
* 减去某个用户的余额
* @param userName
* @param price
*/
public void updateBalance(String userName,int price){
String sql = "update account set balance=balance-? where username=?";
jdbcTemplate.update(sql,price,userName);
}
/**
* 按照图书的id来获取图书的价格
* @param id
* @return
*/
public int getPrice(int id){
String sql = "select price from book where id=?";
return jdbcTemplate.queryForObject(sql,Integer.class,id);
}
/**
* 减库存,减去某本书的库存
* @param id
*/
public void updateStock(int id){
String sql = "update book_stock set stock=stock-1 where id=?";
jdbcTemplate.update(sql,id);
}
/**
* 修改图书价格
* @param id
* @param price
*/
public void updatePrice(int id,int price){
String sql = "update book set price=? where id =?";
jdbcTemplate.update(sql,price,id);
}
}
BookService.java
@Service
public class BookService {
@Autowired
BookDao bookDao;
/**
* 结账:传入哪个用户买了哪本书
* @param username
* @param id
*/
@Transactional(propagation = Propagation.REQUIRED)
public void checkout(String username,int id) {
bookDao.updateStock(id);
int price = bookDao.getPrice(id);
bookDao.updateBalance(username,price);
}
@Transactional(propagation = Propagation.REQUIRED)
public void updatePrice(int id,int price){
bookDao.updatePrice(id,price);
int i = 1/0;
}
}
MulService.java
@Service
public class MulService {
@Autowired
private BookService bookService;
@Transactional
public void mulTx(){
bookService.checkout("zhangsan",1);
bookService.updatePrice(1,1000);
}
}
MyTest.java
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("jdbcTemplate.xml");
MulService mulService = context.getBean("mulService", MulService.class);
mulService.mulTx();
}
}
通过上图的结果发现,如果设置的传播特性是Required,那么所有的事务都会统一成一个事务,一旦发生错误,所有的数据都要进行回滚。
BookService.java
@Service
public class BookService {
@Autowired
BookDao bookDao;
/**
* 结账:传入哪个用户买了哪本书
* @param username
* @param id
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void checkout(String username,int id) {
bookDao.updateStock(id);
int price = bookDao.getPrice(id);
bookDao.updateBalance(username,price);
}
@Transactional(propagation = Propagation.REQUIRED)
public void updatePrice(int id,int price){
bookDao.updatePrice(id,price);
int i = 1/0;
}
}
通过修改checkout方法的传播特性为Required_new,发现价格进行了回滚,而其他的数据没有进行回滚。
BookService.java
@Service
public class BookService {
@Autowired
BookDao bookDao;
/**
* 结账:传入哪个用户买了哪本书
* @param username
* @param id
*/
@Transactional(propagation = Propagation.REQUIRED)
public void checkout(String username,int id) {
bookDao.updateStock(id);
int price = bookDao.getPrice(id);
bookDao.updateBalance(username,price);
}
@Transactional(propagation = Propagation.REQUIRED)
public void updatePrice(int id,int price){
bookDao.updatePrice(id,price);
}
}
MulService.java
@Service
public class MulService {
@Autowired
private BookService bookService;
@Transactional
public void mulTx(){
bookService.checkout("zhangsan",1);
bookService.updatePrice(1,1000);
int i = 1/0;
}
}
将bookservice方法的传播行为为Required,并且将报错设置在MulService中,发现会都进行回滚。
BookService.java
@Service
public class BookService {
@Autowired
BookDao bookDao;
/**
* 结账:传入哪个用户买了哪本书
* @param username
* @param id
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void checkout(String username,int id) {
bookDao.updateStock(id);
int price = bookDao.getPrice(id);
bookDao.updateBalance(username,price);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updatePrice(int id,int price){
bookDao.updatePrice(id,price);
}
}
MulService.java
@Service
public class MulService {
@Autowired
private BookService bookService;
@Transactional
public void mulTx(){
bookService.checkout("zhangsan",1);
bookService.updatePrice(1,1000);
int i = 1/0;
}
}
将bookservice方法的传播行为为Requires_new,并且将报错设置在MulService中,发现都不会进行回滚。
BookService.java
@Service
public class BookService {
@Autowired
BookDao bookDao;
/**
* 结账:传入哪个用户买了哪本书
* @param username
* @param id
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void checkout(String username,int id) {
bookDao.updateStock(id);
int price = bookDao.getPrice(id);
bookDao.updateBalance(username,price);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updatePrice(int id,int price){
bookDao.updatePrice(id,price);
}
@Transactional
public void mulTx(){
checkout("zhangsan",1);
updatePrice(1,1000);
int i = 1/0;
}
}
如果在bookservice执行的话,会发现刚刚的效果就没有了,原因是外层调用的时候使用的AOP,但是本类方法自己的调用就是最最普通的调用,就是同一个事务。
总结:
1、事务传播级别是REQUIRED,当checkout()被调用时(假定被另一类中commit()调用),如果checkout()中的代码抛出异常,即便被捕获,commit()中的其他代码都会roll back
2、是REQUIRES_NEW,如果checkout()中的代码抛出异常,并且被捕获,commit()中的其他代码不会roll back;如果commit()中的其他代码抛出异常,而且没有捕获,不会导致checkout()回滚
3、是NESTED,如果checkout()中的代码抛出异常,并且被捕获,commit()中的其他代码不会roll back;如果commit()中的其他代码抛出异常,而且没有捕获,会导致checkout()回滚
4.PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 "内部" 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行.
5.另一方面, PROPAGATION_NESTED 开始一个 "嵌套的" 事务, 它是已经存在事务的一个真正的子事务. 嵌套事务开始执行时, 它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 嵌套事务是外部事务的一部分, 只有外部事务结束后它才会被提交.
6.由此可见, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大区别在于, PROPAGATION_REQUIRES_NEW 完全是一个新的事务, 而 PROPAGATION_NESTED 则是外部事务的子事务, 如果外部事务 commit, 嵌套事务也会被 commit, 这个规则同样适用于 roll back.
基于xml的事务配置