简介:
事务分为 编程式事务和声明式事务。
编程式事务
通过代码的形式对事物进行设置,对多个表进行增删改,有错进行回滚,正常结束就提交。
Connection connection = JdbcUtils.getConnection();//得到连接 try { //1. 先设置事务不要自动提交,默认true connection.setAutoCommint(false); //2. 进行各种crud //多个表的修改,添加,删除 //3. 提交 connection.commit(); } catch (Exception e) { //4. 回滚 conection.rollback(); }
声明式事务
使用实例用户购买商品。
我们需要去处理用户购买商品的业务逻辑进行分析: 当一个用户要去购买商品应该包含三个步骤。
1.通过商品id 获取价格.
2.购买商品(某人购买商品,修改用户的余额)
3.修改商品库存量
我们需要用到三张表商品表,用户表,商品存量表。三个操作应该同步成功或同步失败,错了一个就会产生严重影响,因此我们应该使用事务处理。
如何解决
1.如果我们使用传统的编程事务来处理,将代码写到一起[缺点: 代码冗余,效率低,不利于扩展, 优点是简单,好理解]。
2.如果使用Spring 的声明式事务处理, 可以将上面三个子步骤分别写成一个方法,然后统一管理.
[这个是Spring 很牛的地方,在开发使用的很多,优点是无代码冗余,效率高,扩展方便,缺点是理解较困难]
==> 底层使用AOP (动态代理+动态绑定+反射+注解) => 看Debug 源码…
声明式事务使用-代码实现
@Repository public class GoodsDao { @Autowired private JdbcTemplate jdbcTemplate; public Float queryPriceById(Integer id) { String sql = "SELECT price From goods Where goods_id=?"; //获取商品价格 Float price = jdbcTemplate.queryForObject(sql, Float.class, id); return price; } public void updateBalance(Integer user_id, Float money) { String sql = "UPDATE user_account SET money=money-? Where user_id=?"; //修改用户余额 jdbcTemplate.update(sql, money, user_id); } public void updateAmount(Integer goods_id, int amount){ String sql = "UPDATE goods_amount SET goods_num=goods_num-? Where goods_id=?"; //修改商品库存 jdbcTemplate.update(sql, amount , goods_id); } }
创建src\tx_ioc.xml 文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 引入外部属性文件--> <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.nlc.spring.tx.dao"/> </beans>
测试单个
public class TxTest { /** * 查询商品的价格 */ @Test public void queryPriceByIdTest() { ApplicationContext ioc = new ClassPathXmlApplicationContext("tx_ioc.xml"); System.out.println("查询价格"); GoodsDao bean = ioc.getBean(GoodsDao.class); Float price = bean.queryPriceById(1); System.out.println(" 1 号商品的价格= " + price); } /** * 修改用户余额(购买商品后) */ @Test public void updateBalanceTest() { ApplicationContext ioc = new ClassPathXmlApplicationContext("tx_ioc.xml"); GoodsDao bean = ioc.getBean(GoodsDao.class); bean.updateBalance(1, 30.5f); System.out.println("====修改余额成功===="); } /** * 测试修改商品的库存量 */ @Test public void updateAmountTest() { ApplicationContext ioc = new ClassPathXmlApplicationContext("tx_ioc.xml"); GoodsDao bean = ioc.getBean(GoodsDao.class); 韩顺平Java 工程师 bean.updateAmount(1, 2); System.out.println("====修改商品库存量成功===="); } }
不使用事务,如果出错就会出现数据不一致现象.
创建GoodsService
@Service public class GoodsService { @Autowired private GoodsDao goodsDao; /** * 购买商品[没有使用事务] * @param user_id * @param goods_id * @param num */ public void buyGoods(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); } }
修改tx_ioc.xml, 加入对Service 的扫描
<!-- 加入自动扫描包service --> <context:component-scan base-package="com.nlc.spring.tx.service"/>
测试TxTest.java, 增加方法, 这时没有加入事务控制
//测试购买商品 @Test public void buyGoodsTest() { ApplicationContext ioc = new ClassPathXmlApplicationContext("tx_ioc.xml"); GoodsService bean = ioc.getBean(GoodsService.class); bean.buyGoods(1, 2, 1); System.out.println("====购买商品成功===="); }
修改GoodsService.java, 增加测试方法,加入声明式事务注解
/** * 购买商品(使用spring 的声明式事务) * 默认事务是传播属性:REQUIRED */ @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); }
修改TxTest.java, 增加测试方法, 对声明式事务进行测试,看看是否保证了数据一致性
// 测试购买商品(使用了声明式事务) @Test public void buyGoodsByTxTest() { ApplicationContext ioc = new ClassPathXmlApplicationContext("tx_ioc.xml"); GoodsService bean = ioc.getBean(GoodsService.class); //使用buyGoodsByTx() bean.buyGoodsByTx(1, 2, 1); System.out.println("====购买商品成功===="); }