传统事务流程:
Connection connection = JdbcUtils.getConnection();
try {
//1. 先设置事务不要自动提交
connection.setAutoCommit(false);
//2. 进行各种 crud
//多个表的修改,添加 ,删除
select from 商品表 => 获取价格
//修改用户余额
update ...
//修改库存量
update
//3. 提交
connection.commit();
} catch (Exception e) {
//4. 回滚
conection.rollback();
}
使用 Spring 的声明式事务处理, 可以将上面三个子步骤分别写成一个方法,然后统一管理
<!-- 引入外部属性文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="user" value="${jdbc.userName}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClass" value="${jdbc.driverClass}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
</bean>
<!-- 配置 JdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 将上面的数据源分配给 jdbcTemplate -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="dataSourceTransactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 开启基于注解的声明式事务功能 -->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
<!-- 加入自动扫描包 dao -->
<context:component-scan base-package="com.hspedu.spring.tx.dao"/>
修改 GoodsService.java,加入声明式事务注解
@Transactional
public void buyGoodsByTx(int user_id, int goods_id, int num) {
//查询到商品价格
Float goods_price = goodsDao.queryPriceById(goods_id);
//购买商品,减去余额
goodsDao.updateBalance(user_id, goods_price * num);
//模拟一个异常, 会发生数据库数据不一致现象
// int i = 10 / 0;
//更新库存
goodsDao.updateAmount(goods_id, num);
}
执行流程:
当有多个事务处理并存时,如何控制?
重点分析?REQUIRED 和 REQUIRED_NEW
如果设置为 REQUIRES_NEW:
buyGoods2 如果错误,不会影响到 buyGoods(),即它们的事务是独立的
如果设置为 REQUIRED:
buyGoods2 和 buyGoods 是一个整体,只要有方法的事务错误,那么两个方法都不会执行成功.!
这个概念参考 MySQL
默认的隔离级别, 就是 mysql 数据库默认的隔离级别 一般为 REPEATABLE_READ
查看数据库默认的隔离级别:
SELECT @@global.tx_isolation
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void buyGoodsByTxISOLATION(int user_id, int goods_id, int num) {
//查询到商品价格
Float goods_price = goodsDao.queryPriceById(goods_id);
System.out.println("第一次读取的价格 =" + goods_price);
//测试一下隔离级别,在同一个事务中,查询一下价格
goods_price = goodsDao.queryPriceById(goods_id);
System.out.println("第二次读取的价格 =" + goods_price);
}
过程:在读完第一次数据后,通过 mysql 进行修改该数据
结果:两次读取到的价格是一样的,不会受到 MySQL?修改影响
语法:
?结果:两次读取到的价格是不一样的,数据是会受到 MySQL?修改影响
目的:如果一个事务执行的时间超过某个时间限制,就让该事务回滚
语法:
超出时间会抛出异常,事务回滚,原来的操作撤销?
注: