百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

spring事物中的传播及隔离(简述spring的事物传播行为和隔离级别)

bigegpt 2024-08-01 12:02 8 浏览

添加事务注解

1、使用 propagation 指定事务的传播行为, 即当前的事务方法被另外一个事务方法调用时。如何使用事务, 默认取值为 REQUIRED, 即使用调用方法的事务REQUIRES_NEW: 事务自己的事务, 调用的事务方法的事务被挂起。

2、使用 isolation 指定事务的隔离级别, 最常用的取值为 READ_COMMITTED。

3、默认情况下 Spring 的声明式事务对所有的运行时异常进行回滚. 也可以通过对应的属性进行设置. 通常情况下去默认值即可。

4、使用 readOnly 指定事务是否为只读. 表示这个事务只读取数据但不更新数据,这样可以帮助数据库引擎优化事务. 若真的是一个只读取数据库值的方法, 应设置 readOnly=true

5、使用 timeout 指定强制回滚之前事务可以占用的时间。

个人疑问有一些,结尾来说,用教程例子来说吧。

我的代码如下:

BookShopDao接口

package
 com.demo.spring.bean;
public
 
interface
 
BookShopDao
 {
 
//根据书的编号返回书的单价
 
public
 
int
 findBookPriceByIsbn(
String
 isbn);
 
//根据书的编号返回输的库存
 
public
 
void
 updateBookStock(
String
 isbn);
 
//更新用户账户余额
 
public
 
void
 updateUserAccount(
String
 username, 
int
 price);
}

实现类

package
 com.demo.spring.bean;
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) {
 
// TODO Auto-generated method stub
 
String
 sql = 
"SELECT price FROM book WHERE isbn=?"
;
 
Integer
 price = jdbcTemplate.queryForObject(sql, 
Integer
.
class
, isbn);
 
return
 price;
 }
 
@Override
 
public
 
void
 updateBookStock(
String
 isbn) {
 
// TODO Auto-generated method stub
 
String
 sql = 
"SELECT stock FROM book_stock WHERE isbn= ? "
;
 
Integer
 stock = jdbcTemplate.queryForObject(sql, 
Integer
.
class
, isbn);
 
if
 (stock == 
0
) {
 
throw
 
new
 
BookStockException
(
"库存不足!!"
);
 }
 
String
 sql1 = 
"UPDATE book_stock SET stock=stock-1 WHERE isbn=?"
;
 jdbcTemplate.update(sql1, isbn);
 }
 
@Override
 
public
 
void
 updateUserAccount(
String
 username, 
int
 price) {
 
// TODO Auto-generated method stub
 
String
 sql1 = 
"SELECT balance FROM account WHERE username= ? "
;
 
Integer
 account = jdbcTemplate.queryForObject(sql1, 
Integer
.
class
,
 username);
 
if
 (account < price) {
 
throw
 
new
 
AccountException
(
"余额不足!!"
);
 }
 
String
 sql = 
"UPDATE account SET balance=balance-? WHERE username=?"
;
 jdbcTemplate.update(sql, price, username);
 }
}

BookShopService接口

package
 com.demo.spring.bean;
public
 
interface
 
BookShopService
 {
//购书
public
 
void
 purchase(
String
 username, 
String
 isbn);
}

实现类

package
 com.demo.spring.bean;
import
 org.springframework.beans.factory.annotation.
Autowired
;
import
 org.springframework.stereotype.
Service
;
import
 org.springframework.transaction.annotation.
Transactional
;
@Service
(
"bookShopService"
)
public
 
class
 
BookShopServiceImpl
 
implements
 
BookShopService
 {
 
@Autowired
 
private
 
BookShopDao
 bookShopDao;
// @Transactional(propagation=Propagation.REQUIRES_NEW,isolation=Isolation.READ_COMMITTED,readOnly=true,timeout=5,noRollbackFor=AccountException.class)
 
@Transactional
 
@Override
 
public
 
void
 purchase(
String
 username, 
String
 isbn) {
 
try
 {
 
Thread
.sleep(
5000
);
 } 
catch
 (
InterruptedException
 e) {
 
// TODO Auto-generated catch block
 e.printStackTrace();
 }
 
// TODO Auto-generated method stub
 
int
 price = bookShopDao.findBookPriceByIsbn(isbn);
 bookShopDao.updateBookStock(isbn);
 bookShopDao.updateUserAccount(username, price);
 }
}

Cashier批量购书接口

package
 com.demo.spring.bean;
import
 java.util.
List
;
public
 
interface
 
Cashier
 {
 
//批量购书
 
public
 
void
 checkout(
String
 username, 
List
<
String
> isbns);
}

实现类

package
 com.demo.spring.bean;
import
 java.util.
List
;
import
 org.springframework.beans.factory.annotation.
Autowired
;
import
 org.springframework.stereotype.
Service
;
@Service
(
"cashier"
)
public
 
class
 
CashierImpl
 
implements
 
Cashier
 {
 
@Autowired
 
private
 
BookShopService
 bookShopService;
// @Transactional
 
@Override
 
public
 
void
 checkout(
String
 username, 
List
<
String
> isbns) {
 
// TODO Auto-generated method stub
 
for
 (
String
 isbn : isbns) {
 bookShopService.purchase(username, isbn);
 }
 }
}

账户余额不足异常(自定义异常)

package
 com.demo.spring.bean;
public
 
class
 
AccountException
 
extends
 
RuntimeException
 {
 
/**
 *
 */
 
private
 
static
 
final
 
long
 serialVersionUID = 
1L
;
 
public
 
AccountException
() {
 
super
();
 
// TODO Auto-generated constructor stub
 }
 
public
 
AccountException
(
String
 message, 
Throwable
 cause,
 
boolean
 enableSuppression, 
boolean
 writableStackTrace) {
 
super
(message, cause, enableSuppression, writableStackTrace);
 
// TODO Auto-generated constructor stub
 }
 
public
 
AccountException
(
String
 message, 
Throwable
 cause) {
 
super
(message, cause);
 
// TODO Auto-generated constructor stub
 }
 
public
 
AccountException
(
String
 message) {
 
super
(message);
 
// TODO Auto-generated constructor stub
 }
 
public
 
AccountException
(
Throwable
 cause) {
 
super
(cause);
 
// TODO Auto-generated constructor stub
 }
}

库存不足异常(自定义异常)

package
 com.demo.spring.bean;
public
 
class
 
BookStockException
 
extends
 
RuntimeException
 {
 
private
 
static
 
final
 
long
 serialVersionUID = 
1L
;
 
public
 
BookStockException
() {
 
super
();
 
// TODO Auto-generated constructor stub
 }
 
public
 
BookStockException
(
String
 message, 
Throwable
 cause,
 
boolean
 enableSuppression, 
boolean
 writableStackTrace) {
 
super
(message, cause, enableSuppression, writableStackTrace);
 
// TODO Auto-generated constructor stub
 }
 
public
 
BookStockException
(
String
 message, 
Throwable
 cause) {
 
super
(message, cause);
 
// TODO Auto-generated constructor stub
 }
 
public
 
BookStockException
(
String
 message) {
 
super
(message);
 
// TODO Auto-generated constructor stub
 }
 
public
 
BookStockException
(
Throwable
 cause) {
 
super
(cause);
 
// TODO Auto-generated constructor stub
 }
}

测试类

package
 com.demo.spring.bean;
import
 java.util.
Arrays
;
import
 org.junit.
Test
;
import
 org.springframework.beans.factory.annotation.
Autowired
;
import
 org.springframework.context.
ApplicationContext
;
import
 org.springframework.context.support.
ClassPathXmlApplicationContext
;
public
 
class
 
MainTest
 {
 
private
 
ApplicationContext
 ctx;
 
@Autowired
 
private
 
Cashier
 cashier;
 {
 ctx=
new
 
ClassPathXmlApplicationContext
(
"bean.xml"
);
 cashier=(
Cashier
) ctx.getBean(
"cashier"
);
 }
 
@Test
 
public
 
void
 test(){
// System.out.println(bookShopDao.findBookPriceByIsbn("1001"));
 cashier.checkout(
"rongrong"
, 
Arrays
.asList(
"1001"
,
"1002"
));
 }
}

bean文件

<?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:aop
=
"http://www.springframework.org/schema/aop"
 
xmlns:context
=
"http://www.springframework.org/schema/context"
 
xmlns:tx
=
"http://www.springframework.org/schema/tx"
 
xsi:schemaLocation
=
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd"
>
 
<!-- 装载自导导入的包 -->
 
<context:component-scan
base-package
=
"com.demo.spring.bean"
></context:component-scan>
 
<!-- 引入外部数据源 -->
 
<context:property-placeholder
 
location
=
"classpath:db.properties"
/>
 
<!-- 配置mysql数据源 -->
 
<bean
 
id
=
"datasource"
 
class
=
"org.springframework.jdbc.datasource.DriverManagerDataSource"
>
 
<property
 
name
=
"driverClassName"
 
value
=
"${db.driverClassName}"
></property>
 
<property
 
name
=
"url"
 
value
=
"${db.url}"
></property>
 
<property
 
name
=
"username"
 
value
=
"${db.username}"
></property>
 
<property
 
name
=
"password"
 
value
=
"${db.password}"
></property>
 
</bean>
 
<!-- 配置jdbc模板 -->
 
<bean
 
id
=
"jdbcTemplate"
 
class
=
"org.springframework.jdbc.core.JdbcTemplate"
>
 
<property
 
name
=
"dataSource"
 
ref
=
"datasource"
></property>
 
</bean>
 
<!-- 配置事务管理器 -->
 
<bean
 
id
=
"transactionManagertest"
 
class
=
"org.springframework.jdbc.datasource.DataSourceTransactionManager"
>
 
<property
 
name
=
"dataSource"
 
ref
=
"datasource"
></property>
 
</bean>
 
 
<!-- 启用事务注解 -->
 
<tx:annotation-driven
 
transaction-manager
=
"transactionManagertest"
/>
</beans>

数据源

db.driverClassName=com.mysql.jdbc.
Driver
db.url=jdbc:mysql:
//localhost:3306/spring
db.username=root
db.password=root



疑问如下:

1、前提条件:用户按照1001,1002这种顺序去购书,调用checkout接口批量购书 @Transactional注解确实满足原子性操作,要么都做,要么不做 但是,我试验了下,如果在public void checkout(String username, Listisbns) 上方不加Transactional注解与在public void purchase(String username, String isbn)上方加@Transactional(propagation=Propagation.REQUIRES_NEW效果一样

2、当账户余额为120,可以满足账户减去1001的单价,1001的库存减1 但是,将账户余额改为80,从单价上看可以买1002那本书,按照上面的顺序去买书,按照上面加上注解@Transactional(propagation=Propagation.REQUIRES_NEW效果一样,根本不好使,我个人觉得因为在更新账户余额,那有个判断先查询1001的书的单价确实大于当前账户余额80,先判断了,所以抛异常后面代码就不走了

3、另外当前账户余额可以买1002这本书,想在不改变购书的顺序情况下,用@Transactional注解实现,可以买1002这本书?,减去当前账户余额80,更新1002书的库存,哪位大神看到,帮我看下,怎么用这个注解实现?

以上为我的个人疑惑的点,有兴趣的同学可以研究下,然后在留言私信给我即可,小编不胜感谢!

附上:sql

/*
Navicat MySQL Data Transfer
Source Server : myTestdata
Source Server Version : 50627
Source Host : localhost:3306
Source Database : spring
Target Server Type : MYSQL
Target Server Version : 50627
File Encoding : 65001
Date: 2017-01-18 11:28:50
*/
SET FOREIGN_KEY_CHECKS=
0
;
-- ----------------------------
-- 
Table
 structure 
for
 account
-- ----------------------------
DROP TABLE IF EXISTS 
`account`
;
CREATE TABLE 
`account`
 (
 
`username`
 varchar(
50
) NOT NULL,
 
`balance`
 
int
(
11
) DEFAULT NULL,
 PRIMARY KEY (
`username`
)
) ENGINE=
InnoDB
 DEFAULT CHARSET=gbk;
-- ----------------------------
-- 
Records
 of account
-- ----------------------------
INSERT INTO 
`account`
 VALUES (
'rongrong'
, 
'300'
);
INSERT INTO 
`account`
 VALUES (
'zhangsan'
, 
'200'
);
-- ----------------------------
-- 
Table
 structure 
for
 book
-- ----------------------------
DROP TABLE IF EXISTS 
`book`
;
CREATE TABLE 
`book`
 (
 
`isbn`
 varchar(
50
) NOT NULL,
 
`book_name`
 varchar(
100
) DEFAULT NULL,
 
`price`
 
int
(
11
) DEFAULT NULL,
 PRIMARY KEY (
`isbn`
)
) ENGINE=
InnoDB
 DEFAULT CHARSET=gbk;
-- ----------------------------
-- 
Records
 of book
-- ----------------------------
INSERT INTO 
`book`
 VALUES (
'1001'
, 
'java'
, 
'100'
);
INSERT INTO 
`book`
 VALUES (
'1002'
, 
'python'
, 
'60'
);
-- ----------------------------
-- 
Table
 structure 
for
 book_stock
-- ----------------------------
DROP TABLE IF EXISTS 
`book_stock`
;
CREATE TABLE 
`book_stock`
 (
 
`isbn`
 varchar(
50
) NOT NULL,
 
`bookname`
 varchar(
100
) DEFAULT NULL,
 
`stock`
 
int
(
11
) DEFAULT NULL,
 PRIMARY KEY (
`isbn`
)
) ENGINE=
InnoDB
 DEFAULT CHARSET=gbk;
-- ----------------------------
-- 
Records
 of book_stock
-- ----------------------------
INSERT INTO 
`book_stock`
 VALUES (
'1001'
, 
'java'
, 
'10'
);
INSERT INTO 
`book_stock`
 VALUES (
'1002'
, 
'python'
, 
'10'
);

以上就是我的对此问题的整理和思考。如果你对此话题有自己的思考和理解,也欢迎留言私信一起探讨!

私信我:“资料”,可免费领取更多学习资料

相关推荐

得物可观测平台架构升级:基于GreptimeDB的全新监控体系实践

一、摘要在前端可观测分析场景中,需要实时观测并处理多地、多环境的运行情况,以保障Web应用和移动端的可用性与性能。传统方案往往依赖代理Agent→消息队列→流计算引擎→OLAP存储...

warm-flow新春版:网关直连和流程图重构

本期主要解决了网关直连和流程图重构,可以自此之后可支持各种复杂的网关混合、多网关直连使用。-新增Ruoyi-Vue-Plus优秀开源集成案例更新日志[feat]导入、导出和保存等新增json格式支持...

扣子空间体验报告

在数字化时代,智能工具的应用正不断拓展到我们工作和生活的各个角落。从任务规划到项目执行,再到任务管理,作者深入探讨了这款工具在不同场景下的表现和潜力。通过具体的应用实例,文章展示了扣子空间如何帮助用户...

spider-flow:开源的可视化方式定义爬虫方案

spider-flow简介spider-flow是一个爬虫平台,以可视化推拽方式定义爬取流程,无需代码即可实现一个爬虫服务。spider-flow特性支持css选择器、正则提取支持JSON/XML格式...

solon-flow 你好世界!

solon-flow是一个基础级的流处理引擎(可用于业务规则、决策处理、计算编排、流程审批等......)。提供有“开放式”驱动定制支持,像jdbc有mysql或pgsql等驱动,可...

新一代开源爬虫平台:SpiderFlow

SpiderFlow:新一代爬虫平台,以图形化方式定义爬虫流程,不写代码即可完成爬虫。-精选真开源,释放新价值。概览Spider-Flow是一个开源的、面向所有用户的Web端爬虫构建平台,它使用Ja...

通过 SQL 训练机器学习模型的引擎

关注薪资待遇的同学应该知道,机器学习相关的岗位工资普遍偏高啊。同时随着各种通用机器学习框架的出现,机器学习的门槛也在逐渐降低,训练一个简单的机器学习模型变得不那么难。但是不得不承认对于一些数据相关的工...

鼠须管输入法rime for Mac

鼠须管输入法forMac是一款十分新颖的跨平台输入法软件,全名是中州韵输入法引擎,鼠须管输入法mac版不仅仅是一个输入法,而是一个输入法算法框架。Rime的基础架构十分精良,一套算法支持了拼音、...

Go语言 1.20 版本正式发布:新版详细介绍

Go1.20简介最新的Go版本1.20在Go1.19发布六个月后发布。它的大部分更改都在工具链、运行时和库的实现中。一如既往,该版本保持了Go1的兼容性承诺。我们期望几乎所...

iOS 10平台SpriteKit新特性之Tile Maps(上)

简介苹果公司在WWDC2016大会上向人们展示了一大批新的好东西。其中之一就是SpriteKitTileEditor。这款工具易于上手,而且看起来速度特别快。在本教程中,你将了解关于TileE...

程序员简历例句—范例Java、Python、C++模板

个人简介通用简介:有良好的代码风格,通过添加注释提高代码可读性,注重代码质量,研读过XXX,XXX等多个开源项目源码从而学习增强代码的健壮性与扩展性。具备良好的代码编程习惯及文档编写能力,参与多个高...

Telerik UI for iOS Q3 2015正式发布

近日,TelerikUIforiOS正式发布了Q32015。新版本新增对XCode7、Swift2.0和iOS9的支持,同时还新增了对数轴、不连续的日期时间轴等;改进TKDataPoin...

ios使用ijkplayer+nginx进行视频直播

上两节,我们讲到使用nginx和ngixn的rtmp模块搭建直播的服务器,接着我们讲解了在Android使用ijkplayer来作为我们的视频直播播放器,整个过程中,需要注意的就是ijlplayer编...

IOS技术分享|iOS快速生成开发文档(一)

前言对于开发人员而言,文档的作用不言而喻。文档不仅可以提高软件开发效率,还能便于以后的软件开发、使用和维护。本文主要讲述Objective-C快速生成开发文档工具appledoc。简介apple...

macOS下配置VS Code C++开发环境

本文介绍在苹果macOS操作系统下,配置VisualStudioCode的C/C++开发环境的过程,本环境使用Clang/LLVM编译器和调试器。一、前置条件本文默认前置条件是,您的开发设备已...