日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

淺談mybatis 樂觀鎖實現,解決并發問題

瀏覽:219日期:2023-10-23 10:11:45

情景展示:

銀行兩操作員同時操作同一賬戶就是典型的例子。

比如A、B操作員同時讀取一余額為1000元的賬戶,A操作員為該賬戶增加100元,B操作員同時為該賬戶扣除50元,A先提交,B后提交。最后實際賬戶余額為1000-50=950元,但本該為1000+100-50=1050。這就是典型的并發問題。

樂觀鎖機制在一定程度上解決了這個問題。樂觀鎖,大多是基于數據版本(Version)記錄機制實現。何謂數據版本?即為數據增加一個版本標識,在基于數據庫表的版本解決方案中,一般是通過為數據庫表增加一個 “version” 字段來實現。

讀取出數據時,將此版本號一同讀出,之后更新時,對此版本號加一。此時,將提交數據的版本數據與數據庫表對應記錄的當前版本信息進行比對,如果提交的數據版本號大于數據庫表當前版本號,則予以更新,否則認為是過期數據。

對于上面修改用戶帳戶信息的例子而言,假設數據庫中帳戶信息表中有一個version字段,當前值為1;而當前帳戶余額字段(balance)為1000元。假設操作員A先更新完,操作員B后更新。

a、操作員A此時將其讀出(version=1),并從其帳戶余額中增加100(1000+100=1100)。

b、在操作員A操作的過程中,操作員B也讀入此用戶信息(version=1),并從其帳戶余額中扣除50(1000-50=950)。

c、操作員A完成了修改工作,將數據版本號加一(version=2),連同帳戶增加后余額(balance=1100),提交至數據庫更新,此時由于提交數據版本大于數據庫記錄當前版本,數據被更新,數據庫記錄version更新為2。

d、操作員B完成了操作,也將版本號加一(version=2)試圖向數據庫提交數據(balance=950),但此時比對數據庫記錄版本時發現,操作員B提交的數據版本號為2,數據庫記錄當前版本也為2,不滿足 “提交版本必須大于記錄當前版本才能執行更新 “的樂觀鎖策略,因此,操作員B的提交被駁回。

這樣,就避免了操作員B用基于version=1的舊數據修改的結果覆蓋操作員A的操作結果的可能。

示例代碼:

account建庫腳本

drop table if exists account_wallet; /*==============================================================*//* Table: account_wallet *//*==============================================================*/create table account_wallet( id int not null comment ’用戶錢包主鍵’, user_open_id varchar(64) comment ’用戶中心的用戶唯一編號’, user_amount decimal(10,5), create_time datetime, update_time datetime, pay_password varchar(64), is_open int comment ’0:代表未開啟支付密碼,1:代表開發支付密碼’, check_key varchar(64) comment ’平臺進行用戶余額更改時,首先效驗key值,否則無法進行用戶余額更改操作’, version int comment ’基于mysql樂觀鎖,解決并發訪問’ primary key (id));

dao層

AccountWallet selectByOpenId(String openId);

int updateAccountWallet(AccountWallet record);

service 層

AccountWallet selectByOpenId(String openId);

int updateAccountWallet(AccountWallet record);

serviceImpl層

public AccountWallet selectByOpenId(String openId) { // TODO Auto-generated method stub return accountWalletMapper.selectByOpenId(openId); } public int updateAccountWallet(AccountWallet record) { // TODO Auto-generated method stub return accountWalletMapper.updateAccountWallet(record); }

sql.xml

<!--通過用戶唯一編號,查詢用戶錢包相關的信息 --> <select resultMap='BaseResultMap' parameterType='java.lang.String'> select <include refid='Base_Column_List' /> from account_wallet where user_open_id = #{openId,jdbcType=VARCHAR} </select> <!--用戶錢包數據更改 ,通過樂觀鎖(version機制)實現 --> <update parameterType='com.settlement.model.AccountWallet'> <![CDATA[ update account_wallet set user_amount = #{userAmount,jdbcType=DECIMAL}, version = version + 1 where id =#{id,jdbcType=INTEGER} and version = #{version,jdbcType=INTEGER} ]]> </update>

controller 層

package com.settlement.controller; import java.math.BigDecimal;import javax.servlet.http.HttpServletRequest;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.ResponseBody;import com.settlement.commons.base.BaseController;import com.settlement.model.AccountWallet;import com.settlement.service.AccountWalletService;import com.taobao.api.internal.util.StringUtils; /** * 用戶錢包Controller * * @author zzg * @date 2017-02-10 */ @Controller@RequestMapping(value = '/wallet')public class WalletController extends BaseController { @Autowiredprivate AccountWalletService accountWalletService; /** * 針對業務系統高并發-----修改用戶錢包數據余額,采用樂觀鎖 * * @return */@RequestMapping(value = '/walleroptimisticlock.action', method = RequestMethod.POST)@ResponseBodypublic String walleroptimisticlock(HttpServletRequest request) { String result = ''; try {String openId = request.getParameter('openId') == null ? null: request.getParameter('openId').trim(); // 用戶唯一編號String openType = request.getParameter('openType') == null ? null: request.getParameter('openType').trim(); // 1:代表增加,2:代表減少String amount = request.getParameter('amount') == null ? null: request.getParameter('amount').trim(); // 金額 if (StringUtils.isEmpty(openId)) {return 'openId is null';}if (StringUtils.isEmpty(openType)) {return 'openType is null';}if (StringUtils.isEmpty(amount)) {return 'amount is null';}AccountWallet wallet = accountWalletService.selectByOpenId(openId); // 用戶操作金額BigDecimal cash = BigDecimal.valueOf(Double.parseDouble(amount));cash.doubleValue();cash.floatValue();if (Integer.parseInt(openType) == 1) {wallet.setUserAmount(wallet.getUserAmount().add(cash));} else if (Integer.parseInt(openType) == 2) {wallet.setUserAmount(wallet.getUserAmount().subtract(cash));} int target = accountWalletService.updateAccountWallet(wallet);System.out.println('修改用戶金額是否:' + (target == 1 ? '成功' : '失敗')); } catch (Exception e) {result = e.getMessage();return result;} return 'success';} }

模擬并發訪問

package com.settlement.concurrent; import java.text.SimpleDateFormat;import java.util.Date;import java.util.concurrent.CountDownLatch; import com.settlement.commons.utils.HttpRequest; /** * 模擬用戶的并發請求,檢測用戶樂觀鎖的性能問題 * * @author zzg * @date 2017-02-10 */public class ConcurrentTest {final static SimpleDateFormat sdf=new SimpleDateFormat('yyyy-MM-dd HH:mm:ss'); public static void main(String[] args){CountDownLatch latch=new CountDownLatch(1);//模擬5人并發請求,用戶錢包for(int i=0;i<5;i++){//模擬5個用戶AnalogUser analogUser = new AnalogUser('user'+i,'58899dcd-46b0-4b16-82df-bdfd0d953bfb','1','20.024',latch);analogUser.start();}latch.countDown();//計數器?p一 所有線程釋放 并發訪問。System.out.println('所有模擬請求結束 at '+sdf.format(new Date())); }static class AnalogUser extends Thread{String workerName;//模擬用戶姓名String openId;String openType;String amount;CountDownLatch latch;public AnalogUser(String workerName, String openId, String openType, String amount,CountDownLatch latch) {super();this.workerName = workerName;this.openId = openId;this.openType = openType;this.amount = amount;this.latch = latch;}@Overridepublic void run() {// TODO Auto-generated method stubtry { latch.await(); //一直阻塞當前線程,直到計時器的值為0 } catch (InterruptedException e) { e.printStackTrace(); } post();//發送post 請求 } public void post(){String result = '';System.out.println('模擬用戶: '+workerName+' 開始發送模擬請求 at '+sdf.format(new Date())); result = HttpRequest.sendPost('http://localhost:8080/Settlement/wallet/walleroptimisticlock.action', 'openId='+openId+'&openType='+openType+'&amount='+amount);System.out.println('操作結果:'+result);System.out.println('模擬用戶: '+workerName+' 模擬請求結束 at '+sdf.format(new Date())); }}}

補充知識:Mybatis-plus代碼生成器,自用版本不帶xml

package com.wuyd.mybatispulsdemo;import com.baomidou.mybatisplus.annotation.DbType;import com.baomidou.mybatisplus.generator.AutoGenerator;import com.baomidou.mybatisplus.generator.config.*;import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;/** * @author wuyd * 創建時間:2019/10/8 11:17 */public class CodeGenerator { public static void main(String[] args) { AutoGenerator mpg = new AutoGenerator(); //全局配置 GlobalConfig gc = new GlobalConfig(); gc.setOutputDir(System.getProperty('user.dir')+'/src/main/java'); gc.setFileOverride(true); //不需要ActiveRecord特性的請改為false gc.setActiveRecord(true); gc.setSwagger2(true); gc.setAuthor('wuyd'); //自定義文件命名,注意%s 會自動填充表實體屬性 gc.setControllerName('%sController'); gc.setServiceName('%sService'); gc.setServiceImplName('%sServiceImpl'); gc.setEntityName('%sEntity'); gc.setMapperName('%sMapper'); mpg.setGlobalConfig(gc); //數據源配置 DataSourceConfig dsc = new DataSourceConfig(); dsc.setDbType(DbType.MYSQL); dsc.setDriverName('com.mysql.jdbc.Driver'); dsc.setUsername('xxx'); dsc.setPassword('xxx'); dsc.setUrl('jdbc:mysql://xxx.xxx.xxx.xxx:3306/xxxxx?useUnicode=true&useSSL=false&characterEncoding=utf8'); mpg.setDataSource(dsc); //策略配置 StrategyConfig strategy = new StrategyConfig(); //此處可以修改您的表前綴 strategy.setTablePrefix(new String[]{}); //表名生成策略 strategy.setNaming(NamingStrategy.underline_to_camel); strategy.setColumnNaming(NamingStrategy.underline_to_camel); //需要生成的表 strategy.setInclude(new String[]{'knapsacks','knapsacks_kind','knapsacks_prop','knapsacks_recharge_card'}); strategy.setSuperServiceClass(null); strategy.setSuperServiceImplClass(null); strategy.setSuperMapperClass(null); strategy.setControllerMappingHyphenStyle(true); strategy.setEntityLombokModel(true); strategy.setEntitySerialVersionUID(true); strategy.setEntityTableFieldAnnotationEnable(true); mpg.setStrategy(strategy); // 配置模板 TemplateConfig templateConfig = new TemplateConfig(); templateConfig.setXml(null); mpg.setTemplate(templateConfig); //包配置 PackageConfig pc = new PackageConfig(); pc.setParent('com.wuyd.mybatispulsdemo'); pc.setController('controller'); pc.setService('service'); pc.setServiceImpl('service.impl'); pc.setMapper('mapper'); pc.setEntity('entity'); mpg.setPackageInfo(pc); //執行生成 mpg.execute(); }}

pom.xml

<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.2.0</version> </dependency> <!-- ORM 選一款 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>${mybatis-plus-boot-starter.version}</version> </dependency> <!-- Mysql驅動 注意版本!--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql-connector.version}</version> </dependency> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>2.1</version> </dependency>

參考列表

官網代碼生成器部分

以上這篇淺談mybatis 樂觀鎖實現,解決并發問題就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持好吧啦網。

標簽: Mybatis 數據庫
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
伊人网在线播放| 欧美日韩亚洲一区在线观看| 激情中国色综合| 久久久亚洲欧洲日产| 国产精品日韩精品中文字幕| 国产亚洲欧美日韩精品一区二区三区 | 91欧美极品| 欧美三区不卡| 国产精品videossex久久发布 | 亚洲乱亚洲高清| 日韩影院免费视频| 亚洲日本久久| 国产精品久久久久9999高清| 麻豆久久一区二区| 国产suv精品一区| 色爱综合网欧美| 91精品一区二区三区综合在线爱 | 国产精选在线| 国产一区二区中文| 水蜜桃久久夜色精品一区的特点| 蜜桃久久精品一区二区| 日韩高清不卡在线| 激情中国色综合| 免费久久久久久久久| 亚洲精品自拍| 老牛国内精品亚洲成av人片| 日韩在线不卡| 亚洲经典在线| 日韩va亚洲va欧美va久久| 麻豆91小视频| 不卡av一区二区| 日本va欧美va瓶| 日韩1区在线| 首页亚洲欧美制服丝腿| 国产精品一区二区av日韩在线| 伊人久久在线| 免费在线欧美视频| 国产精品嫩草影院在线看| 国产福利片在线观看| 日韩一级精品| 国产精品一区免费在线| 精品丝袜在线| 一区二区精品| 欧美激情国产在线| 热久久国产精品| 国产不卡一区| 综合色一区二区| av中文资源在线资源免费观看| 麻豆精品网站| 国产一区二区亚洲| 首页国产欧美日韩丝袜| 精品国产亚洲日本| 亚洲一区二区三区免费在线观看| 国产精品成人**免费视频 | 水蜜桃精品av一区二区| 免费日韩一区二区| 国产福利亚洲| 久久国产99| 91一区二区三区四区| 亚洲2区在线| 91精品国产乱码久久久久久久| 69堂免费精品视频在线播放| 亚洲va在线| 欧美黄色一区二区| 日韩亚洲国产欧美| 国产一区二区三区久久| 蜜桃一区二区三区在线观看| 日韩欧美一区二区三区在线视频| 日韩国产欧美在线视频| 婷婷精品进入| 天堂√8在线中文| 日韩精品亚洲专区| 欧美99久久| 中文字幕人成乱码在线观看 | 亚洲精品福利| 欧美日一区二区| 免费日韩成人| 日本不卡的三区四区五区| 国产综合亚洲精品一区二| 精品一区二区三区免费看| 在线亚洲一区| 99久久婷婷| 精品一区电影| 91亚洲精品在看在线观看高清| 红桃视频亚洲| 韩国久久久久久| 国产精品s色| 蜜桃视频免费观看一区| 亚洲性视频h| 日韩欧美精品| 亚洲视频综合| 久久影院一区二区三区| 国产精品大片免费观看| 欧美激情福利| 久久免费视频66| 国产一区丝袜| 蜜桃视频在线网站| 欧美日韩精品免费观看视完整| 亚洲国产成人二区| 精品免费av| а√在线中文在线新版| 成人精品动漫一区二区三区| 色婷婷综合网| 日本高清不卡一区二区三区视频| 999精品色在线播放| 蜜桃国内精品久久久久软件9| 在线视频观看日韩| 一本一本久久| 97久久中文字幕| 国产精品午夜一区二区三区| 久久av影院| 欧美gv在线| 一区二区亚洲精品| 一本一道久久a久久| 日韩国产欧美在线播放| 久久国产人妖系列| 久久中文在线| 秋霞影视一区二区三区| 国产综合婷婷| 亚洲免费福利一区| 国产精品大片| 四虎4545www国产精品| 婷婷丁香综合| 日韩中文字幕| 麻豆国产精品777777在线| 欧美日韩视频网站| 亚洲欧美日本国产专区一区| 青青草91久久久久久久久| 久久伊人久久| 欧美精品激情| 视频一区日韩精品| 精品视频久久| 亚洲大全视频| 日韩超碰人人爽人人做人人添| 卡一卡二国产精品| 99视频精品全部免费在线视频| 蜜桃视频一区二区三区| 国产精品.xx视频.xxtv| 日韩另类视频| 婷婷亚洲精品| 吉吉日韩欧美| 免费看欧美美女黄的网站| 国产精品美女久久久久久不卡| 日本欧美不卡| 亚洲综合小说| 久久中文欧美| 欧美一区=区| 精品网站999| 中国女人久久久| 久久精品免视看国产成人| 国产在线不卡| 欧美精品影院| 三级精品视频| 欧美片第1页综合| 久久久久久久久丰满| 日韩av午夜在线观看| 天堂а√在线最新版中文在线| 日韩精品一二三区| 久久影视三级福利片| 久久av一区| 国产精品久久777777毛茸茸| 日本国产精品| 国产欧美一区| 国产精品视区| 国产伊人久久| 喷白浆一区二区| 日韩免费在线| 国产精品综合| 麻豆91精品| 国产精品亚洲一区二区三区在线观看| 婷婷五月色综合香五月| 亚洲精品一区三区三区在线观看| 91成人精品观看| 午夜国产一区二区| 美女久久久精品| 午夜在线精品偷拍| 国产不卡人人| 国产精品一区二区精品| 丝袜a∨在线一区二区三区不卡| 亚洲最新无码中文字幕久久| 欧美亚洲一级| 亚洲丝袜美腿一区| 亚洲成a人片| 麻豆精品视频在线观看视频| 中文字幕日韩高清在线| 99久久www免费| 丰满少妇一区| 久久精品99久久久| 亚洲一区二区三区在线免费| 女人av一区| 91日韩免费| 欧美黄色一区| 日韩av资源网| 美国欧美日韩国产在线播放| 欧美99久久| 999国产精品999久久久久久| 高清一区二区三区| 美女尤物国产一区| 国产欧美一区二区三区精品观看| 亚洲精品成人一区|