举例:
用户想要买两本书,但是账户的钱只够支付一本书,这时需要先支付一本。
如果事默认的事务传播行为(REQUIRED),则两本书都不能购买成功,这是需要将事务的传播行为设置为REQUIRES_NEW
重点事这两种传播行为:
REQUIRED 传播行为:
默认的事务传播行为,当前方法内的所有方法都在一个事务中,要么都成功,要么都不成功。
@Transactional(propagation=Propagation.REQUIRED)
REQUIRED_NEW传播行为:
它表示该方法必须启动一个新事务, 并在自己的事务内运行. 如果有事务在运行, 就应该先挂起它.
@Transactional(propagation=Propagation.REQUIRES_NEW)
事务传播行为在配置文件中配置:
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRES_NEW"/>
</tx:attributes>
</tx:advice>
下面放上具体的例子:
模拟用户买书,如果一个用户想要买两本书,但是账户余额不足时,可以让用户先买一本
package com.nxmu.spring.TransactionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository("bookShopDao")
public class BookShopDaoImpl implements BookShopDao{
@Autowired
private JdbcTemplate jdbcTemplate;
/**
* 查询书的价格
*/
@Override
public int findBookPriceByIsbn(String isbn) {
String sql = "select price from book where isbn = ?";
int price = jdbcTemplate.queryForObject(sql, Integer.class,isbn);
return price;
}
/**
* 更新书的数量
*/
@Override
public void updateBookStock(String isbn) {
String sql2 = "select stock from book_stock where isbn = ?";
int stock = jdbcTemplate.queryForObject(sql2, Integer.class,isbn);
if (stock == 0) {
throw new BookStockException("库存不足");
}
String sql = "update book_stock set stock = stock - 1 where isbn = ?";
jdbcTemplate.update(sql,isbn);
}
/**
* 更新账户余额
*/
@Override
public void updateUserAccount(String username, int price) {
String sql2 = "select balance from account where username = ?";
int balance = jdbcTemplate.queryForObject(sql2, Integer.class,username);
if (balance < price) {
throw new UserAccountException("余额不足");
}
String sql = "update account set balance = balance - ? where username = ?";
jdbcTemplate.update(sql,price,username);
}
}
这里将购买每本书的方法事务传播行为设置为REQUIRED_NEW
@Service("bookShopService")
public class BookShopServiceImpl implements BookShopService {
@Autowired
private BookShopDao bookShopDao;
@Transactional(propagation=Propagation.REQUIRES_NEW)
@Override
public void purchese(String username, String isbn) {
int price = bookShopDao.findBookPriceByIsbn(isbn);
bookShopDao.updateBookStock(isbn);
bookShopDao.updateUserAccount(username, price);
}
}
下面的类时用来一次购买多本书,传入用户名和需要购买书的id的list,对list进行遍历,每一次便利就是购买一本书,每一次购买书就时一次事务
@Service("cashier")
public class CachierImpl implements Cashier {
@Autowired
private BookShopService bookShopService;
@Transactional(propagation=Propagation.REQUIRED)
@Override
public void checkout(String username, List<String> isbns) {
for (String isbn : isbns) {
bookShopService.purchese(username, isbn);
}
}
}
下面时测试方法:
@Test
void testCheckout() {
List<String> isbns = new ArrayList<>();
cashier.checkout("AA", Arrays.asList("1001","1002"));
}