事务的作用:保持数据的一致性和完整性。
在学习数据库中,也到事务,只不过每个事务都需要,开启事务、提交事务、回滚事务、释放资源的代码,这种方式叫做“编程式事务管理”。
那么另一种事务管理方式,将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理,Spring声明式事务管理建立在AOP基础之上,是一个典型的横切关注点,通过环绕增强来实现,其原理是对方法前后进行拦截,然后在目标方法开始之前创建或加入一个事务,在执行完毕之后根据执行情况提交或回滚事务。这种方式是“声明式事务管理”。
如何使用声明式事务管理?
1、添加spring-aspects-4.3.10.RELEASE.jar包和aspectjweaver-x.x.x.jar包
2、在Spring配置文件中添加如下配置:
<context:component-scan base-package="com.jd"></context:component-scan>
<bean id="datasource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close" p:password="root" p:username="root">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/test"></property>
</bean>
<bean class="org.springframework.jdbc.core.JdbcTemplate" p:dataSource-ref="datasource"></bean>
<!-- 配置数据源事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource"></property>
</bean>
<!-- 开启事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
3、在Service层public方法上添加事务注解——@Transactional
通过以下例子进行更好的理解:
背景:以下代码可以实现,钱够书够就可以“购买成功!”但是会出现书够钱不够的情况,此时也会对书籍表进行个更新!
@Service
public class CouponService implements ICouponService {
@Autowired
private CouponDao couponDao;
@Autowired
private BookDao bookDao;
@Autowired
private MoneyDao moneyDao;
@Override
public boolean insert(String userId, String bookId, int count){
if(bookDao.enough(bookId, count)) {//书是否足够?updata
bookDao.updata(bookId, count);
}
double price = bookDao.getPrice(bookId);
double total = price * count;
if(moneyDao.enough(userId, total)) {//钱是否足够?uodata
moneyDao.updata(userId, total);
}
String id = UUID.randomUUID().toString();
couponDao.insert(id, bookId, userId, total);
//
return true;
}
此时加上@Transcational注解,防止出现书够钱不够的情况:
书籍表中有10本书籍,每本书10元,一个人钱包有10元,欲买3本,则该行代码抛出MoneyException异常,但由于该异常为运行时异常,所以回滚事.
结果如下:
注意:
如果是检查时异常需要在@Transactional注解中添加了rollbackFor=MoneyException.class,才可以回滚事务!
timeout:设置一个事务所允许执行的最长时长(单位:秒),如果超过该时长且事务还没有完成,则自动回滚事务且出现org.springframework.transaction.TransactionTimedOutException异常.
代码改动如下:
@Override
@Transactional(timeout=3)
public boolean insert(String userId, String bookId, int count){
if(bookDao.enough(bookId, count)) {//书是否足够?updata
bookDao.updata(bookId, count);
}
double price = bookDao.getPrice(bookId);
double total = price * count;
if(moneyDao.enough(userId, total)) {//钱是否足够?uodata
//改动如下:延时4秒,但是timeout是3秒,所以会报异常
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
moneyDao.updata(userId, total);
}
String id = UUID.randomUUID().toString();
couponDao.insert(id, bookId, userId, total);
//
return true;
}
结果如下: