并发实际场景(保持余额操作的正确:数据库余额字段版)

2023-12-27 12:20

本文主要是介绍并发实际场景(保持余额操作的正确:数据库余额字段版),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

场景:

一个人在一家银行办了一个账户,银行给了 一张卡(存取款)、一本存折(存取款)、一个网银(查询余额)

卡和存储不断存款和取款,网银不断查询余额。如何保持余额的正确。

 

数据库余额表:原本想用版本号来实现的,后面弃用version字段。

DROP TABLE IF EXISTS `t_test`;
CREATE TABLE `t_test` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `account` decimal(11,2) DEFAULT NULL,
  `version` int(100) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t_test
-- ----------------------------
INSERT INTO `t_test` VALUES ('1', '50.00', '1');

mapper.xml文件:仔细看两个sql的写法,这里是重点,请不要在java代码中进行余额的加减操作。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.taotao.mapper.TTestMapper" ><resultMap id="BaseResultMap" type="com.taotao.pojo.TTest" ><id column="id" property="id" jdbcType="INTEGER" /><result column="account" property="account" jdbcType="DECIMAL" /><result column="version" property="version" jdbcType="INTEGER" /></resultMap><update id="updateAccountAdd" parameterType="com.taotao.pojo.TTest" >update t_testset account = account + #{newAccount,jdbcType=DECIMAL}where id = #{id,jdbcType=INTEGER}</update><update id="updateAccountSub" parameterType="com.taotao.pojo.TTest" >update t_testset account = account - #{newAccount,jdbcType=DECIMAL}where id = #{id,jdbcType=INTEGER} and account >= #{newAccount,jdbcType=DECIMAL}</update></mapper>

dao暂时不贴出:

service:请在每个方法上加入事物和synchronized。

@Service
public class TestServiceImpl implements TestService {@Autowiredprivate TTestMapper testMapper;/*** 存钱** @param money*/@Override@Transactionalpublic synchronized BigDecimal addAcount(String name, int money) throws TransactionalException {TTest tTest = testMapper.selectByPrimaryKey(1);tTest.setNewAccount(new BigDecimal(money));int i = testMapper.updateAccountAdd(tTest);if (i == 0){System.out.println("添加余额失败!余额=" + tTest.getAccount());return new BigDecimal(money);}System.out.println(name + "...存入:" + money + "..." + Thread.currentThread().getName());return selectAcount(name);}/*** 取钱** @param money*/@Override@Transactionalpublic synchronized BigDecimal subAcount(String name, int money) throws TransactionalException{TTest tTest = testMapper.selectByPrimaryKey(1);tTest.setNewAccount(new BigDecimal(money));int i = testMapper.updateAccountSub(tTest);if (i == 0){System.out.println("账户余额不足!余额=" + tTest.getAccount());return new BigDecimal(money);}System.out.println(name + "...取出:" + money + "..." + Thread.currentThread().getName());return selectAcount(name);}/*** 查询余额*/@Override@Transactionalpublic synchronized BigDecimal selectAcount(String name) throws TransactionalException{TTest tTest = testMapper.selectByPrimaryKey(1);System.out.println(name + "...余额:" + tTest.getAccount());return tTest.getAccount();}
}

controller:

@Controller
public class TestMysqlController {@Autowiredprivate TestService testService;@RequestMapping(value="/cardAddAcountMysql")@ResponseBodypublic TaotaoResult<Integer> cardAddAcount() throws TransactionalException{TaotaoResult<Integer> result = new TaotaoResult<Integer>();result.setData("+100, 余额: " + testService.addAcount("card", 100));return  result;}@RequestMapping(value="/passbookAddAcountMysql")@ResponseBodypublic TaotaoResult<Integer> passbookAddAcount() throws TransactionalException{TaotaoResult<Integer> result = new TaotaoResult<Integer>();result.setData("+100, 余额: " + testService.addAcount("存折", 100));return  result;}@RequestMapping(value="/cardSubAcountMysql")@ResponseBodypublic TaotaoResult<Integer> cardSubAcount(){TaotaoResult<Integer> result = new TaotaoResult<Integer>();result.setData("-150, 余额: " + testService.subAcount("card", 150));return  result;}@RequestMapping(value="/passbookSubAcountMysql")@ResponseBodypublic TaotaoResult<Integer> passbookSubAcount() throws TransactionalException{TaotaoResult<Integer> result = new TaotaoResult<Integer>();result.setData("-200, 余额: " + testService.subAcount("存折", 200));return  result;}@RequestMapping(value="/selectAcountMysql")@ResponseBodypublic TaotaoResult<Integer> selectAcount() throws TransactionalException {TaotaoResult<Integer> result = new TaotaoResult<Integer>();result.setData(testService.selectAcount(""));return  result;}}

执行结果:

card...余额:2850.00

card...取出:150...http-apr-8085-exec-38

card...余额:2700.00

存折...取出:200...http-apr-8085-exec-104

存折...余额:2500.00

存折...取出:200...http-apr-8085-exec-73

存折...余额:2300.00

存折...取出:200...http-apr-8085-exec-105

存折...余额:2100.00

存折...取出:200...http-apr-8085-exec-120

存折...余额:1900.00

存折...取出:200...http-apr-8085-exec-39

存折...余额:1700.00

存折...取出:200...http-apr-8085-exec-107

存折...余额:1500.00

card...取出:150...http-apr-8085-exec-108

card...余额:1350.00

card...取出:150...http-apr-8085-exec-116

card...余额:1200.00

card...取出:150...http-apr-8085-exec-117

card...余额:1050.00

存折...取出:200...http-apr-8085-exec-111

存折...余额:850.00

存折...取出:200...http-apr-8085-exec-119

存折...余额:650.00

存折...取出:200...http-apr-8085-exec-115

存折...余额:450.00

存折...取出:200...http-apr-8085-exec-123

存折...余额:250.00

存折...取出:200...http-apr-8085-exec-54

存折...余额:50.00

账户余额不足!余额=50.00

账户余额不足!余额=50.00

账户余额不足!余额=50.00

账户余额不足!余额=50.00

账户余额不足!余额=50.00

账户余额不足!余额=50.00

账户余额不足!余额=50.00

账户余额不足!余额=50.00

账户余额不足!余额=50.00

账户余额不足!余额=50.00

账户余额不足!余额=50.00

账户余额不足!余额=50.00

...余额:50.00

...余额:50.00

...余额:50.00

...余额:50.00

账户余额不足!余额=50.00

账户余额不足!余额=50.00

账户余额不足!余额=50.00

账户余额不足!余额=50.00

...余额:50.00

...余额:50.00

...余额:50.00

账户余额不足!余额=50.00

账户余额不足!余额=50.00

账户余额不足!余额=50.00

...余额:50.00

...余额:50.00

...余额:50.00

账户余额不足!余额=50.00

账户余额不足!余额=50.00

账户余额不足!余额=50.00

...余额:50.00

...余额:50.00

...余额:50.00

账户余额不足!余额=50.00

...余额:50.00

...余额:50.00

...余额:50.00

...余额:50.00

...余额:50.00

...余额:50.00

...余额:50.00

...余额:50.00

...余额:50.00

...余额:50.00

账户余额不足!余额=50.00

...余额:50.00

...余额:50.00

...余额:50.00

...余额:50.00

...余额:50.00

...余额:50.00

...余额:50.00

 

测试用例:

链接:https://pan.baidu.com/s/1YuH8FTu9SX4DxVYNaOL9Lg 密码:4vgr

 

这篇关于并发实际场景(保持余额操作的正确:数据库余额字段版)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/543110

相关文章

SQL Server修改数据库名及物理数据文件名操作步骤

《SQLServer修改数据库名及物理数据文件名操作步骤》在SQLServer中重命名数据库是一个常见的操作,但需要确保用户具有足够的权限来执行此操作,:本文主要介绍SQLServer修改数据... 目录一、背景介绍二、操作步骤2.1 设置为单用户模式(断开连接)2.2 修改数据库名称2.3 查找逻辑文件名

SQL Server数据库死锁处理超详细攻略

《SQLServer数据库死锁处理超详细攻略》SQLServer作为主流数据库管理系统,在高并发场景下可能面临死锁问题,影响系统性能和稳定性,这篇文章主要给大家介绍了关于SQLServer数据库死... 目录一、引言二、查询 Sqlserver 中造成死锁的 SPID三、用内置函数查询执行信息1. sp_w

SQL中JOIN操作的条件使用总结与实践

《SQL中JOIN操作的条件使用总结与实践》在SQL查询中,JOIN操作是多表关联的核心工具,本文将从原理,场景和最佳实践三个方面总结JOIN条件的使用规则,希望可以帮助开发者精准控制查询逻辑... 目录一、ON与WHERE的本质区别二、场景化条件使用规则三、最佳实践建议1.优先使用ON条件2.WHERE用

Python主动抛出异常的各种用法和场景分析

《Python主动抛出异常的各种用法和场景分析》在Python中,我们不仅可以捕获和处理异常,还可以主动抛出异常,也就是以类的方式自定义错误的类型和提示信息,这在编程中非常有用,下面我将详细解释主动抛... 目录一、为什么要主动抛出异常?二、基本语法:raise关键字基本示例三、raise的多种用法1. 抛

Linux链表操作方式

《Linux链表操作方式》:本文主要介绍Linux链表操作方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、链表基础概念与内核链表优势二、内核链表结构与宏解析三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势六、典型应用场景七、调试技巧与

Java Multimap实现类与操作的具体示例

《JavaMultimap实现类与操作的具体示例》Multimap出现在Google的Guava库中,它为Java提供了更加灵活的集合操作,:本文主要介绍JavaMultimap实现类与操作的... 目录一、Multimap 概述Multimap 主要特点:二、Multimap 实现类1. ListMult

Spring组件实例化扩展点之InstantiationAwareBeanPostProcessor使用场景解析

《Spring组件实例化扩展点之InstantiationAwareBeanPostProcessor使用场景解析》InstantiationAwareBeanPostProcessor是Spring... 目录一、什么是InstantiationAwareBeanPostProcessor?二、核心方法解

Java 枚举的基本使用方法及实际使用场景

《Java枚举的基本使用方法及实际使用场景》枚举是Java中一种特殊的类,用于定义一组固定的常量,枚举类型提供了更好的类型安全性和可读性,适用于需要定义一组有限且固定的值的场景,本文给大家介绍Jav... 目录一、什么是枚举?二、枚举的基本使用方法定义枚举三、实际使用场景代替常量状态机四、更多用法1.实现接

Python中文件读取操作漏洞深度解析与防护指南

《Python中文件读取操作漏洞深度解析与防护指南》在Web应用开发中,文件操作是最基础也最危险的功能之一,这篇文章将全面剖析Python环境中常见的文件读取漏洞类型,成因及防护方案,感兴趣的小伙伴可... 目录引言一、静态资源处理中的路径穿越漏洞1.1 典型漏洞场景1.2 os.path.join()的陷

java -jar命令运行 jar包时运行外部依赖jar包的场景分析

《java-jar命令运行jar包时运行外部依赖jar包的场景分析》:本文主要介绍java-jar命令运行jar包时运行外部依赖jar包的场景分析,本文给大家介绍的非常详细,对大家的学习或工作... 目录Java -jar命令运行 jar包时如何运行外部依赖jar包场景:解决:方法一、启动参数添加: -Xb