事务
概述
在数据库中,有时要把多个步骤的命令当作一个整体来运行,这个整体要么全部成功,要么全部失败。这就需要用到事务。
在计算机中,事务(Transaction),是访问并可能更新数据库中各种数据项的一个程序执行单元。事务由事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作组成。
每个事务都要满足 ACID 特性:
- 原子性(Atomicity):表示事务的执行要么全部完成,要么全都不做。一个事务对数据库的所有操作是一个不可分割的操作序列
- 一致性(Consistency):表示数据库中的数据总能保持在正确的状态
- 隔离性(Isolation):表示事务内部的操作及使用的数据对其他并发事务是隔离的,并发执行的两个或多个事务可以同时运行而互不影响
- 持久性(Durability):表示事务一旦完成,对数据库中数据的改变就会永久性保存
@Transactional注解:
- propagation属性:设置传播行为,一般在一个存在事务的方法调用另一个也存在事务的方法时使用(当值为Propagation.REQUIRED,使用同一个事务;值为Propagation.REQUIRES_NEW,使用各自事务)
- isolation属性:设置隔离级别,一般默认
- rollbackFor属性:设置控制回滚,一般默认
- readOnly属性:设置事务是否可读,一般查询方法设置为 True ,其余为 False (默认值)
- timeout属性:设置事务超时后强制回滚
简单示例:
Spring 的声明式事务(基于注解)
基本逻辑:
获取用户余额、书本价格和数量;
判断余额或数量不足时抛出异常;
用户正常购买书本后,相应减少书本数量和用户余额
首先,创建两张数据表
用户表结构设计:
书本表结构设计:
再根据两张数据表定义与之相映射的实体类:
packagecn.edu.springdemo.transactionDemo.model;//用户表publicclassUser{privateint id;privateString name;privateDouble balance;//余额publicUser(){super();}publicUser(int id,String name,Double balance){this.id = id;this.name = name;this.balance = balance;}publicintgetId(){return id;}publicStringgetName(){return name;}publicDoublegetBalance(){return balance;}publicvoidsetId(int id){this.id = id;}publicvoidsetName(String name){this.name = name;}publicvoidsetBalance(Double balance){this.balance = balance;}@OverridepublicStringtoString(){return"User{"+"id="+ id +", name='"+ name +'\''+", balance="+ balance +'}';}}
//书本表packagecn.edu.springdemo.transactionDemo.model;publicclassBook{privateint id;privateString name;//书名privateDouble price;//价格privateint amount;//数量publicBook(){super();}publicBook(int id,String name,Double price,int amount){this.id = id;this.name = name;this.price = price;this.amount = amount;}publicintgetId(){return id;}publicStringgetName(){return name;}publicDoublegetPrice(){return price;}publicintgetAmount(){return amount;}publicvoidsetId(int id){this.id = id;}publicvoidsetName(String name){this.name = name;}publicvoidsetPrice(Double price){this.price = price;}publicvoidsetAmount(int amount){this.amount = amount;}@OverridepublicStringtoString(){return"Book{"+"id="+ id +", name='"+ name +'\''+", price="+ price +", amount="+ amount +'}';}}
接着,在 dao 层定义两个接口,声明相应的方法:
packagecn.edu.springdemo.transactionDemo.dao;publicinterfaceUserDao{publicdoublegetBalance(int user_id);//通过用户编号获取相应的余额publicvoidupdateBalance(int user_id,double price);//获取价格并修改用户相应的余额}
packagecn.edu.springdemo.transactionDemo.dao;publicinterfaceBookDao{publicintgetAmount(int book_id);//通过书本编号获取相应书本的数量publicdoublegetPrice(int book_id);//通过书本编号获取相应书本的价格publicvoidupdateAmount(int book_id,int amount);//获取购买数量并修改相应书本的数量}
创建接口对应的实现类:
packagecn.edu.springdemo.transactionDemo.dao;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.jdbc.core.JdbcTemplate;importorg.springframework.stereotype.Repository;@Repository("userDao")publicclassUserDaoImplimplementsUserDao{@AutowiredprivateJdbcTemplate jdbcTemplate;//通过JdbcTemplate操作数据库@OverridepublicdoublegetBalance(int user_id){String sql ="SELECT `balance` FROM `user` WHERE `id`=?;";return jdbcTemplate.queryForObject(sql,Double.class,user_id);}@OverridepublicvoidupdateBalance(int user_id,double price){if(getBalance(user_id)< price){thrownewRuntimeException("余额不足");//抛出异常}String sql ="UPDATE `user` SET `balance`=`balance`-? WHERE `id`=?;";
jdbcTemplate.update(sql,price,user_id);}}
packagecn.edu.springdemo.transactionDemo.dao;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.jdbc.core.JdbcTemplate;importorg.springframework.stereotype.Repository;@Repository("bookDao")publicclassBookDaoImplimplementsBookDao{@AutowiredprivateJdbcTemplate jdbcTemplate;@OverridepublicintgetAmount(int book_id){String sql ="SELECT `amount` FROM `book` WHERE `id`=?;";return jdbcTemplate.queryForObject(sql,Integer.class,book_id);}@OverridepublicdoublegetPrice(int book_id){String sql ="SELECT `price` FROM `book` WHERE `id`=?;";return jdbcTemplate.queryForObject(sql,Double.class,book_id);}@OverridepublicvoidupdateAmount(int book_id,int amount){if(getAmount(book_id)< amount){thrownewRuntimeException("数量不足");}String sql ="UPDATE `book` SET `amount`=`amount`-? WHERE `id`=?;";
jdbcTemplate.update(sql,amount,book_id);}}
再在 service 层创建一个 OrderService 接口,声明一个购买的方法:
packagecn.edu.springdemo.transactionDemo.service;publicinterfaceOrderService{publicvoidorder(int user_id,int book_id,int amount);//购买(用户id、书本id、购买数量)}
创建该接口对应的实现类:
packagecn.edu.springdemo.transactionDemo.service;importcn.edu.springdemo.transactionDemo.dao.BookDao;importcn.edu.springdemo.transactionDemo.dao.UserDao;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;importorg.springframework.transaction.annotation.Transactional;@Service("orderService")publicclassOrderServiceImplimplementsOrderService{@AutowiredprivateBookDao bookDao;@AutowiredprivateUserDao userDao;@Transactional@Overridepublicvoidorder(int user_id,int book_id,int amount){//获取书本价格double price = bookDao.getPrice(book_id);//获取书本总价格double sum = price*amount;//购买//修改余额
userDao.updateBalance(user_id,sum);//修改数量
bookDao.updateAmount(book_id,amount);}}
然后,操作数据库。在上一章基础下,xml 配置再添加以下内容:
<?xml version="1.0" encoding="UTF-8"?><beansxmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd"><!-- 配置事务管理器 --><beanid="dataSourceTransactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!-- 配置dataSource --><propertyname="dataSource"ref="dataSource"></property></bean><!-- 开启事务注解 --><tx:annotation-driventransaction-manager="dataSourceTransactionManager"/></beans>
最后,测试结果:
packagecn.edu.springdemo.test;importcn.edu.springdemo.transactionDemo.service.OrderService;importorg.springframework.context.ApplicationContext;importorg.springframework.context.support.ClassPathXmlApplicationContext;publicclassTransactionTest{publicstaticvoidmain(String[] args){ApplicationContext applicationContext =newClassPathXmlApplicationContext("jdbc.xml");OrderService orderService =(OrderService) applicationContext.getBean("orderService");
orderService.order(10101,20230612,2);//用户购买两本书,相应处理}}
执行前,结果如图:
执行后,结果如图:
异常测试,结果如图:
版权归原作者 啊Q老师 所有, 如有侵权,请联系我们删除。