boot入门思想 spring_SpringBoot入门建站全系列(二十一)Mybatis使用乐观锁与悲观锁...

news/2024/5/12 23:04:10/文章来源:https://blog.csdn.net/weixin_36462845/article/details/112297126

0ba1817ab6a2718d88089f0a8e8ecfb7.png

SpringBoot入门建站全系列(二十一)Mybatis使用乐观锁与悲观锁

一、概述

之前有两篇《SpringBoot入门建站全系列(三)Mybatis操作数据库》和《SpringBoot入门建站全系列(四)Mybatis使用进阶篇:动态SQL与分页》介绍了Springboot如何结合Mybatis进行数据库访问操作。这一篇介绍下springboot环境下Mybatis如何进行乐观锁、悲观锁的使用。

悲观锁和乐观锁的概念:

  • 悲观锁:就是独占锁,不管读写都上锁了。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。
  • 乐观锁:不上锁,读取的时候带版本号,写入的时候带着这个版本号,如果不一致就失败,乐观锁适用于多读的应用类型,因为写多的时候会经常失败。

代码可以在Springboot组件化构建https://www.pomit.cn/java/spring/springboot.html中的MybatisLock组件中查看,并下载。

首发地址:

品茗IT-同步发布

如果大家正在寻找一个java的学习环境,或者在开发中遇到困难,可以加入我们的java学习圈,点击即可加入,共同学习,节约学习时间,减少很多在学习中遇到的难题。

二、配置

本文假设你已经引入spring-boot-starter-web。已经是个SpringBoot项目了,如果不会搭建,可以打开这篇文章看一看《SpringBoot入门建站全系列(一)项目建立》。

2.1 Maven依赖

需要引入spring-boot-starter-data-jpa,这里要访问数据库,所以要依赖数据库相关jar包。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-dbcp2</artifactId></dependency>

2.2 配置文件

在application.properties 中需要添加下面的配置:

spring.datasource.type=org.apache.commons.dbcp2.BasicDataSource
spring.datasource.dbcp2.max-wait-millis=60000
spring.datasource.dbcp2.min-idle=20
spring.datasource.dbcp2.initial-size=2
spring.datasource.dbcp2.validation-query=SELECT 1
spring.datasource.dbcp2.connection-properties=characterEncoding=utf8
spring.datasource.dbcp2.validation-query=SELECT 1
spring.datasource.dbcp2.test-while-idle=true
spring.datasource.dbcp2.test-on-borrow=true
spring.datasource.dbcp2.test-on-return=falsespring.datasource.driverClassName = com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/boot?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
spring.datasource.username=cff
spring.datasource.password=123456mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

这里面,包含了数据库连接信息、数据源的连接池配置信息、mybatis配置信息。

  • spring.datasource.dbcp2是配置dbcp2的连接池信息;
  • spring.datasource.type指明数据源的类型;
  • 最上面的http://spring.datasource.xxx指明数据库连接池信息;
  • mybatis.configuration.log-impl指明mybatis的日志打印方式

三、悲观锁

悲观锁在数据库的访问中使用,表现为:前一次请求没执行完,后面一个请求就一直在等待。

3.1 Dao层

数据库要实现悲观锁,就是将sql语句带上for update即可。 for update 是行锁

所在mybatis的查询sql加上for update,就实现了对当前记录的锁定,就实现了悲观锁。

UserInfoDao :

package com.cff.springbootwork.mybatislock.dao;import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;import cn.pomit.springwork.mybatislock.domain.UserInfo;@Mapper
public interface UserInfoDao {@Select({"<script>","SELECT ","user_name as userName,passwd,name,mobile,valid, user_type as userType, version as version","FROM user_info_test","WHERE user_name = #{userName,jdbcType=VARCHAR} for update","</script>"})UserInfo findByUserNameForUpdate(@Param("userName") String userName);@Update({"<script>"," update user_info_test set"," name = #{name, jdbcType=VARCHAR}, mobile = #{mobile, jdbcType=VARCHAR},version=version+1 "," where user_name=#{userName}","</script>"})int update(UserInfo userInfo);@Insert({"<script>","INSERT INTO user_info_test","( user_name,","name ,","mobile,","passwd,","version",") "," values ","( #{userName},","#{name},","#{mobile},","#{passwd},","#{version}"," ) ","</script>"})int save(UserInfo entity);
}

这里,findByUserNameForUpdate的sql中加上了for update。update就是普通的更新而已。

3.2 Service层

更新数据库前,先调用findByUserNameForUpdate方法,使上面的配置的悲观锁锁定表记录,然后再更新。

UserInfoService :

package com.cff.springbootwork.mybatislock.service;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;import cn.pomit.springwork.mybatislock.domain.UserInfo;
import cn.pomit.springwork.mybatislock.mapper.UserInfoDao;@Service
public class UserInfoService {@AutowiredUserInfoDao userInfoDao;public void save(UserInfo entity) {entity.setVersion(0);userInfoDao.save(entity);}@Transactionalpublic UserInfo getUserInfoByUserNamePessimistic(String userName) {return userInfoDao.findByUserNameForUpdate(userName);}@Transactionalpublic void updateWithTimePessimistic(UserInfo entity, int time) throws InterruptedException {      UserInfo userInfo = userInfoDao.findByUserNameForUpdate(entity.getUserName());if (userInfo == null)return;if (!StringUtils.isEmpty(entity.getMobile())) {userInfo.setMobile(entity.getMobile());}if (!StringUtils.isEmpty(entity.getName())) {userInfo.setName(entity.getName());}Thread.sleep(time * 1000L);userInfoDao.update(userInfo);}@Transactionalpublic void updatePessimistic(UserInfo entity) {UserInfo userInfo = userInfoDao.findByUserNameForUpdate(entity.getUserName());if (userInfo == null)return;if (!StringUtils.isEmpty(entity.getMobile())) {userInfo.setMobile(entity.getMobile());}if (!StringUtils.isEmpty(entity.getName())) {userInfo.setName(entity.getName());}userInfoDao.update(userInfo);}}

测试中,我们在update方法中sleep几秒,其他线程的update将一直等待。

3.3 测试Web层

可以先调用/update/{time}接口,延迟执行,然后马上调用/update接口,会发现,/update接口一直在等待/update/{time}接口执行完成。

MybatisPessLockRest :

package com.cff.springbootwork.mybatislock.web;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;import cn.pomit.springwork.mybatislock.domain.UserInfo;
import cn.pomit.springwork.mybatislock.service.UserInfoService;/*** 测试悲观锁* * @author fufei**/
@RestController
@RequestMapping("/mybatispesslock")
public class MybatisPessLockRest {@AutowiredUserInfoService userInfoService;@RequestMapping(value = "/detail/{name}", method = { RequestMethod.GET })public UserInfo detail(@PathVariable("name") String name) {return userInfoService.getUserInfoByUserNamePessimistic(name);}@RequestMapping(value = "/save")public String save(@RequestBody UserInfo userInfo) throws InterruptedException {userInfoService.save(userInfo);return "0000";}@RequestMapping(value = "/update/{time}")public String update(@RequestBody UserInfo userInfo, @PathVariable("time") int time) throws InterruptedException {userInfoService.updateWithTimePessimistic(userInfo, time);return "0000";}@RequestMapping(value = "/update")public String update(@RequestBody UserInfo userInfo) throws InterruptedException {userInfoService.updatePessimistic(userInfo);return "0000";}
}

四、乐观锁

数据库访问dao层还是3.1那个UserInfoDao。

4.1 Dao层

UserInfoDao更新时,需要携带version字段进行更新:and version = #{version}。如果version不一致,是不会更新成功的,这时候,我们的select查询是不能带锁的。

UserInfoDao :

package com.cff.springbootwork.mybatislock.dao;import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;import cn.pomit.springwork.mybatislock.domain.UserInfo;@Mapper
public interface UserInfoDao {@Select({"<script>","SELECT ","user_name as userName,passwd,name,mobile,valid, user_type as userType, version as version","FROM user_info_test","WHERE user_name = #{userName,jdbcType=VARCHAR}","</script>"})UserInfo findByUserName(@Param("userName") String userName);@Update({"<script>"," update user_info_test set"," name = #{name, jdbcType=VARCHAR}, mobile = #{mobile, jdbcType=VARCHAR},version=version+1 "," where user_name=#{userName} and version = #{version}","</script>"})int updateWithVersion(UserInfo userInfo);@Insert({"<script>","INSERT INTO user_info_test","( user_name,","name ,","mobile,","passwd,","version",") "," values ","( #{userName},","#{name},","#{mobile},","#{passwd},","#{version}"," ) ","</script>"})int save(UserInfo entity);
}

4.2 Service层

service层我们做一下简单的调整。更新数据库前,先调用findByUserName方法,查询出当前的版本号,然后再更新。

UserInfoService :

package com.cff.springbootwork.mybatislock.service;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;import cn.pomit.springwork.mybatislock.domain.UserInfo;
import cn.pomit.springwork.mybatislock.mapper.UserInfoDao;@Service
public class UserInfoService {@AutowiredUserInfoDao userInfoDao;public UserInfo getUserInfoByUserName(String userName){return userInfoDao.findByUserName(userName);}public void save(UserInfo entity) {entity.setVersion(0);userInfoDao.save(entity);}@Transactionalpublic void updateWithTimeOptimistic(UserInfo entity, int time) throws Exception {      UserInfo userInfo = userInfoDao.findByUserName(entity.getUserName());if (userInfo == null)return;if (!StringUtils.isEmpty(entity.getMobile())) {userInfo.setMobile(entity.getMobile());}if (!StringUtils.isEmpty(entity.getName())) {userInfo.setName(entity.getName());}Thread.sleep(time * 1000L);int ret = userInfoDao.updateWithVersion(userInfo);if(ret < 1)throw new Exception("乐观锁导致保存失败");}@Transactionalpublic void updateOptimistic(UserInfo entity) throws Exception {UserInfo userInfo = userInfoDao.findByUserName(entity.getUserName());if (userInfo == null)return;if (!StringUtils.isEmpty(entity.getMobile())) {userInfo.setMobile(entity.getMobile());}if (!StringUtils.isEmpty(entity.getName())) {userInfo.setName(entity.getName());}int ret = userInfoDao.updateWithVersion(userInfo);if(ret < 1)throw new Exception("乐观锁导致保存失败");}}

4.2 测试Web层

可以先调用/update/{time}接口,延迟执行,然后马上调用/update接口,会发现,/update接口不会等待/update/{time}接口执行完成,读取完版本号能够成功更新数据,但是/update/{time}接口等待足够时间以后,更新的时候会失败,因为它的版本和数据库的已经不一致了。

注意: 这里更新失败不会抛异常,但是返回值会是0,即更新不成功,需要自行判断。jpa的乐观锁可以抛出异常,手动catch到再自行处理。

MybatisOptiLockRest :

package com.cff.springbootwork.mybatislock.web;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;import cn.pomit.springwork.mybatislock.domain.UserInfo;
import cn.pomit.springwork.mybatislock.service.UserInfoService;/*** 测试乐观锁* @author fufei**/
@RestController
@RequestMapping("/mybatislock")
public class MybatisOptiLockRest {@AutowiredUserInfoService userInfoService;@RequestMapping(value = "/detail/{name}", method = { RequestMethod.GET })public UserInfo detail(@PathVariable("name") String name) {return userInfoService.getUserInfoByUserName(name);}@RequestMapping(value = "/save")public String save(@RequestBody UserInfo userInfo) throws InterruptedException {userInfoService.save(userInfo);return "0000";}@RequestMapping(value = "/update/{time}")public String update(@RequestBody UserInfo userInfo, @PathVariable("time") int time) throws Exception {userInfoService.updateWithTimeOptimistic(userInfo, time);return "0000";}@RequestMapping(value = "/update")public String update(@RequestBody UserInfo userInfo) throws Exception {userInfoService.updateOptimistic(userInfo);return "0000";}
}

五、过程中用到的完整实体和Service

UserInfo:

UserInfoService :

UserInfoDao:

详细完整的实体及代码,可以访问品茗IT-博客《SpringBoot入门建站全系列(二十一)Mybatis使用乐观锁与悲观锁》进行查看

品茗IT-博客专题:https://www.pomit.cn/lecture.html汇总了Spring专题、Springboot专题、SpringCloud专题、web基础配置专题。

快速构建项目

Spring组件化构建

SpringBoot组件化构建

SpringCloud服务化构建

喜欢这篇文章么,喜欢就加入我们一起讨论SpringBoot使用吧!

3da77326c9a3660a3b55009dae2958a1.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_822002.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

cdn对php网站的加速效果,配置七牛自定义域名cdn加速,让你的网站飞起来

之前发过一篇是没有备案的&#xff0c;使用测试域名来缓存静态文件&#xff0c;效果也是杠杠的。如果你还没有备案&#xff0c;先看这篇文章吧&#xff0c;域名没备案使用七牛cdn加速 。步入正题了(为了节约博主发文的时间&#xff0c;尽量少用文字&#xff0c;多以图片展示&am…

360网站卫士能防服务器吗,360网站卫士测评

最近小编的主机商在主机控制后台开通了360网站卫士的功能&#xff0c;在了解了一些关于360网站卫士的功能之后&#xff0c;本站开始试用这款用于保障免受攻击&#xff0c;在开通一周之后&#xff0c;小编就来所说对这款360网站卫士应用的试用感受!首先让小编感受最为深刻的是“…

服务器绑定网站域名,服务器网站绑定域名

服务器网站绑定域名 内容精选换一换这个是浏览器的设置&#xff0c;没绑定SSL证书的&#xff0c;访问都会有这个提示。您可以为您的网站添加SSL证书。不一定。如果您需要通过HTTPS协议访问网站&#xff0c;就需要添加SSL证书&#xff0c;云速建站支持第三方免费SSL证书的添加。…

大型网站架构技术演进(史上最全)

1. 概述 本文以淘宝作为例子&#xff0c;介绍从一百个并发到千万级并发情况下服务端的架构的演进过程&#xff0c;同时列举出每个演进阶段会遇到的相关技术&#xff0c;让大家对架构的演进有一个整体的认知&#xff0c;文章最后汇总了一些架构设计的原则。 2. 基本概念 在介…

制作网站代码_网站制作的六个流程,一个都不能少

随着时代的发展&#xff0c;有新企业的诞生必然会伴随着旧企业的倒闭&#xff0c;新企业的成立必然会有网站制作的需要&#xff0c;那么这个网站制作的作用就不言而喻了&#xff0c;可以宣传企业的文化&#xff0c;加强企业在互联网上的传播。当然了&#xff0c;新建站的公司必…

python django 如何部署_【实战演练】Python+Django网站开发系列02-Django完整开发环境部署...

#本文欢迎转载&#xff0c;转载请注明出处和作者。上一篇我们讲到已经安装好了python3.8.1与django框架&#xff0c;并且直接启动了服务&#xff0c;但是目前并没有任何我们自定义的内容。我们网站开发的最终目标&#xff0c;是开发出一个简单的学生选课管理系统。系统需求&…

设计电子商务网站建设方案_成都婚纱摄影网站建设规划设计方案!

婚姻生活是人生道路中的一件大事儿&#xff0c;现阶段婚纱摄影行业相关的企业慢慢转型&#xff0c;大家对婚纱照的要求也逐渐转型为人性化、多元化,伴随着互联网的转型及当代生活的节奏,传统式的拍摄方法已逐渐被替代,成都婚纱摄影网站建设也是能快速获取顾客关注度十分重要的一…

网站迁移到新服务器的步骤_怎样将网站迁移至云服务器?需要注意什么?

经历过虚拟主机、VPS的使用&#xff0c;也慢慢从什么都不太熟悉的新用户变成了老油条&#xff0c;我知道&#xff0c;很多人把网站从日渐熟悉的传统服务器迁移到云服务器时&#xff0c;心里或多或少不是滋味&#xff0c;有不舍&#xff0c;当然更多的是未知的担忧。所以&#x…

eclipse复制代码连接数据库404_什么是404页面,404页面对网站有什么影响

我们在浏览网页的时候&#xff0c;经常会在打开网页的时候发现上面大大的写着一个 “404”字样的页面&#xff0c;而且显示“你所浏览的页面已经不在了”这样的页面到底有什么样的含义呢&#xff1f;又对网站有什么样的影响&#xff1f;404页面是客户端在浏览网页时&#xff0c…

oracle19删除之前的主目录用户_百度搜索暂停快照删除服务,是否影响SEO优化?...

短视频&#xff0c;自媒体&#xff0c;达人种草一站服务最近据百度官方公告显示&#xff0c;百度百度快照投诉删除入口目前暂停关闭&#xff0c;官方系统批量删除快照业务暂时停止服务&#xff0c;未来网页快照删除和更新问题&#xff0c;需站长们个人手动到百度网页搜索结果底…

textview 大小自适应_上海自适应网站建设注意要点有哪些-回声网络

上海自适应网站建设注意要点有哪些&#xff1f;随着智能手机的普及&#xff0c;移动互联网发展也越来越快速。对于企业来说&#xff0c;仅仅只有一个PC站肯定是不行了&#xff0c;而为了节约成本和方便管理&#xff0c;自适应网站建设也就越来越受欢迎。相较于普通的PC站&#…

nicetool好工具_上进青年研习社丨这6个工具合集网站,抵过上百个软件!

文丨小渔不知道你们有没有这样的经历&#xff1a;为了一些小的需求&#xff0c;在手机上或是电脑上下了一堆软件。虽然用过觉得也挺好用的&#xff0c;但是平时的使用频率真的不高啊。等你把它们都卸载了&#xff0c;过了一段时间突然又需要用它了。所以&#xff0c;今天小渔要…

百度小程序读取服务器数据,百度小程序如何进行异步获取网站数据

近期百度小程序是非常热门的&#xff0c;越来越多的企业都开始加入到百度小程序的正营来&#xff0c;对于百度小程序的开发过程中也难免需要去请求网站的数据&#xff0c;其实对于百度小程序请求网站的数据和微信小程序请求网站的数据操作是一样的也没有太大的区别。下面站优云…

python 小说爬虫_初次尝试python爬虫,爬取小说网站的小说。

本次是小阿鹏&#xff0c;第一次通过python爬虫去爬一个小说网站的小说。 下面直接上菜。 1.首先我需要导入相应的包&#xff0c;这里我采用了第三方模块的架包&#xff0c;requests。requests是python实现的简单易用的HTTP库&#xff0c;使用起来比urllib简洁很多&#xff0c;…

windows server 2008为什么制作的网站打不开_【U盘】小巧实用开源免费的U盘系统启动盘制作工具...

排版|设计|配图©孤狼小航小巧实用开源免费的U盘系统启动盘制作工具和格式化U盘的小工具&#xff0c;它可以快速将ISO镜像文件制作成可引导的USB启动安装盘&#xff0c;支持Windows或Linux启动&#xff0c;堪称写入镜像速度最快的U盘系统制作工具。获取方式将网址直接复制浏…

php网站记事本,php实现记事本案例

本文实例为大家分享了php实现记事本的具体代码&#xff0c;供大家参考&#xff0c;具体内容如下记事本案例要求&#xff1a;1)页面上有一个文本域(textarea元素)&#xff0c;和一个发表按钮2)用户在文本域中输入内容后&#xff0c;点击发表按钮&#xff0c;会以当天的日期和时间…

python 爬网站 实例_不会编程的厨师,不是一名好老公。两周零基础入门python+selenium,实例爬取企查查数据...

春节已过&#xff0c;疫情未过&#xff0c;老婆已然复工&#xff0c;我仍然家里蹲&#xff0c;成天大把的时间&#xff0c;除了研究下喜欢的家常菜&#xff0c;总在寻思着干点啥。这不&#xff0c;老婆天天跟我念叨银行压力大&#xff0c;他们零售苦逼&#xff0c;领导又是变态…

iis php 无法访问此网站_实战经验:php-cgi.exe 进程意外退出

问题描述碰到这么一个问题&#xff1a;IIS上跑PHP&#xff0c;已经在IIS中添加FastCGI模块映射&#xff0c;当IIS的应用程序池配置为DefaultAppPool时执行phpinfo()正常。但是当IIS的应用程序池配置为自己创建的AppPool时&#xff0c;会出现php-cgi.exe进程意外退出。解决方法修…

建站模板论坛_建站那些事儿:新手站长建站初期都要做哪些事儿?

建立自己的网站一个多月了&#xff0c;目前网站更新页面300多信息&#xff0c;百度收录二十多条&#xff0c;跟大家分享以下建站初期在建立网站的第一个月我都做了哪些事儿吧&#xff01;seo信息查询1、域名的注册、服务器的租赁、域名的备案。现在的域名服务商很多&#xff0c…

centos php5.5,PHP安装 - CentOS 5.5下构建LAMP环境_服务器应用_Linux公社-Linux系统门户网站...

第三部分 PHP安装安装前先安装一些库依赖关系。不然预编译不过去。#yum –y install libpng libpng-devel libjpeg libjpeg-devel freetype freetype-devel freetype-demos解压、进入源码目录#tar zxvf php-5.4.0.tar.gz#cd php-5.4.0./configure --prefix/usr/local/php \ …