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

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

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

添加事务注解

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'
);

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

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

相关推荐

当Frida来“敲”门(frida是什么)

0x1渗透测试瓶颈目前,碰到越来越多的大客户都会将核心资产业务集中在统一的APP上,或者对自己比较重要的APP,如自己的主业务,办公APP进行加壳,流量加密,投入了很多精力在移动端的防护上。而现在挖...

服务端性能测试实战3-性能测试脚本开发

前言在前面的两篇文章中,我们分别介绍了性能测试的理论知识以及性能测试计划制定,本篇文章将重点介绍性能测试脚本开发。脚本开发将分为两个阶段:阶段一:了解各个接口的入参、出参,使用Python代码模拟前端...

Springboot整合Apache Ftpserver拓展功能及业务讲解(三)

今日分享每天分享技术实战干货,技术在于积累和收藏,希望可以帮助到您,同时也希望获得您的支持和关注。架构开源地址:https://gitee.com/msxyspringboot整合Ftpserver参...

Linux和Windows下:Python Crypto模块安装方式区别

一、Linux环境下:fromCrypto.SignatureimportPKCS1_v1_5如果导包报错:ImportError:Nomodulenamed'Crypt...

Python 3 加密简介(python des加密解密)

Python3的标准库中是没多少用来解决加密的,不过却有用于处理哈希的库。在这里我们会对其进行一个简单的介绍,但重点会放在两个第三方的软件包:PyCrypto和cryptography上,我...

怎样从零开始编译一个魔兽世界开源服务端Windows

第二章:编译和安装我是艾西,上期我们讲述到编译一个魔兽世界开源服务端环境准备,那么今天跟大家聊聊怎么编译和安装我们直接进入正题(上一章没有看到的小伙伴可以点我主页查看)编译服务端:在D盘新建一个文件夹...

附1-Conda部署安装及基本使用(conda安装教程)

Windows环境安装安装介质下载下载地址:https://www.anaconda.com/products/individual安装Anaconda安装时,选择自定义安装,选择自定义安装路径:配置...

如何配置全世界最小的 MySQL 服务器

配置全世界最小的MySQL服务器——如何在一块IntelEdison为控制板上安装一个MySQL服务器。介绍在我最近的一篇博文中,物联网,消息以及MySQL,我展示了如果Partic...

如何使用Github Action来自动化编译PolarDB-PG数据库

随着PolarDB在国产数据库领域荣膺桂冠并持续获得广泛认可,越来越多的学生和技术爱好者开始关注并涉足这款由阿里巴巴集团倾力打造且性能卓越的关系型云原生数据库。有很多同学想要上手尝试,却卡在了编译数据...

面向NDK开发者的Android 7.0变更(ndk android.mk)

订阅Google官方微信公众号:谷歌开发者。与谷歌一起创造未来!受Android平台其他改进的影响,为了方便加载本机代码,AndroidM和N中的动态链接器对编写整洁且跨平台兼容的本机...

信创改造--人大金仓(Kingbase)数据库安装、备份恢复的问题纪要

问题一:在安装KingbaseES时,安装用户对于安装路径需有“读”、“写”、“执行”的权限。在Linux系统中,需要以非root用户执行安装程序,且该用户要有标准的home目录,您可...

OpenSSH 安全漏洞,修补操作一手掌握

1.漏洞概述近日,国家信息安全漏洞库(CNNVD)收到关于OpenSSH安全漏洞(CNNVD-202407-017、CVE-2024-6387)情况的报送。攻击者可以利用该漏洞在无需认证的情况下,通...

Linux:lsof命令详解(linux lsof命令详解)

介绍欢迎来到这篇博客。在这篇博客中,我们将学习Unix/Linux系统上的lsof命令行工具。命令行工具是您使用CLI(命令行界面)而不是GUI(图形用户界面)运行的程序或工具。lsoflsof代表&...

幻隐说固态第一期:固态硬盘接口类别

前排声明所有信息来源于网络收集,如有错误请评论区指出更正。废话不多说,目前固态硬盘接口按速度由慢到快分有这几类:SATA、mSATA、SATAExpress、PCI-E、m.2、u.2。下面我们来...

新品轰炸 影驰SSD多款产品登Computex

分享泡泡网SSD固态硬盘频道6月6日台北电脑展作为全球第二、亚洲最大的3C/IT产业链专业展,吸引了众多IT厂商和全球各地媒体的热烈关注,全球存储新势力—影驰,也积极参与其中,为广大玩家朋友带来了...