JPA 加锁机制及@Version版本控制方式
作者:black-ant 发布时间:2022-10-06 10:57:58
标签:JPA,加锁,@Version
JPA的加锁机制有两种,乐观锁和悲观锁。
乐观锁:
乐观锁的特点在于认为数据冲突或者更新丢失等情况是很少发生的.当发生的时候,抛出异常和回滚就足够解决问题.
悲观锁:
悲观锁的逻辑在于认为每次数据操作都很有可能发生冲突,所以一开始就获得记录的锁,再进行记录的操作是解决问题的优先选择.
一 简述悲观锁的用法
悲观锁通常是SQL级别的,通过读写时先拿到锁实现,在SQL语句中就会有体现.
1.1 EntityManager 用法
return em.createQuery(sql 语句).setLockMode(LockModeType.NONE).getResultList();
//分解写法大概是:
Query query = getSession().createQuery(hql);
query.setLockMode(LockModeType.NONE);
EntityManager 是一个辅助类,createQuery后返回的就是一个Query对象,然后通过
setLockMode设置锁的级别即可.
LockModeType 类型 | 解释 |
---|---|
LockMode.READ | 事务的隔离级别是Repeatable Read或Serializable时,请求读取数据库记录时自动获得 |
LockMode.WRITE | 请求插入或更新数据库记录时自动获得 |
LockMode.OPTIMISTIC | 乐观锁 |
LockMode.OPTIMISTIC_FORCE_INCREMENT | 乐观锁,通过version控制 |
LockMode.PESSIMISTIC_READ | 与LockMode.PESSIMISTIC_WRITE相同 |
LockMode.PESSIMISTIC_WRITE | 事务开始即获得数据库的锁 |
LockMode.PESSIMISTIC_FORCE_INCREMENT | 事务开始即设置version |
LockMode.NONE | 取消任何锁,如事务结束后的所有对象,或执行了Session的update()、 |
二 乐观锁的详细用法
乐观锁本篇的主要内容
实体类是关键 , 乐观锁常用方法是通过version来控制 ,
数据库对应的表中需要有一个字段(名字随意),字段类型设置成BigInt即可
业务不对该字段进行控制,字段的控制交由系统处理
每一次修改都会导致version递增
当出现同时获得该记录的对象且均需要修改时,当第一个已经提交事务,version字段发生改变,后面提交的事务发现version版本不对,则无法提交,抛出异常
实体类(注意其中的@Version注解)
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String username;
private String userdesc;
@Version
private Long version;
public User() {
}
public User(String username, String userdesc) {
this.username = username;
this.userdesc = userdesc;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getUserDesc() {
return userdesc;
}
public void setUserDesc(String userdesc) {
this.userdesc = userdesc;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
}
controller中通过sleep将线程沉睡,测试事务的提交性
@RestController
public class UserController {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
UserService userService;
@PostMapping("/changeone")
@Transactional
public String changeone() {
User user = userService.findUser("gang");
try {
logger.info("修改1 before:user--{}--Versdion:{}", user.getUserDesc(), user.getVersion());
Thread.sleep(25000);
user.setUserDesc("修改1");
logger.info("修改1 :user--{}--version:{}", user.getUserDesc(), user.getVersion());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (Exception e) {
logger.info("eeeeeeeeeeeeee");
e.printStackTrace();
}
return "true";
}
@PostMapping("/changetwo")
@Transactional
public String changetwo() {
User user = userService.findUser("gang");
try {
logger.info("修改2 before:user--{}--version:{}", user.getUserDesc(), user.getVersion());
Thread.sleep(30000);
user.setUserDesc("修改2");
logger.info("修改2:user--{}--version:{}", user.getUserDesc(), user.getVersion());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (Exception e) {
logger.info("eeeeeeeeeeeeee");
e.printStackTrace();
}
return "true";
}
@PostMapping("/changethree")
@Transactional
public String changethree() {
User user = userService.findUser("gang");
logger.info("修改3 before:user--{}--version:{}", user.getUserDesc(), user.getVersion());
user.setUserDesc("修改3");
logger.info("修改3 :user--{}--version:{}", user.getUserDesc(), user.getVersion());
return "true";
}
@PostMapping("/newuser")
@Transactional
public String newuser() {
logger.info("save user");
User user = new User();
user.setUserDesc("第一次创建");
user.setUsername("gang");
userService.saveUser(user);
return "true";
}
}
以及service及repository
@Service
public class UserService {
@Autowired
UserRepository userRepository;
public User findUser(String username){
return userRepository.findByUsername(username);
}
public void saveUser(User user){
userRepository.save(user);
}
}
UserRepository
public interface UserRepository extends JpaRepository<User,Long> {
User findByUsername(String username);
}
来源:https://blog.csdn.net/zzg19950824/article/details/85468318


猜你喜欢
- android大家都有很多需要用户上传头像的需求,有的是选方形,有的是圆角矩形,有的是圆形。首先我们要做一个处理图片的自定义控件,把传入的图
- 1. 你可以讲下进程与线程的区别?为什么要用多线程?进程:进程是程序的一次执行过程,是系统运行程序的基本单位。线程:单个进程中执行中每个任务
- 前言前面文章讲了消息是如何保存的以及consumeQueue与Index文件更新机制。随着消息的增加,Broker不可能一直保存所有消息,B
- 一、首先看下Android开发用到的sdk目录:build-tools保存着一些Android平台相关通用工具,比如adb、和aapt、ai
- 微软官方的MSDN上说async和await是“异步”,但是不少人(包括笔者自己)都有一些误区需要澄清:为什么await语句之后没有执行?不
- 背景项目中要实现横向列表的无限循环滚动,自然而然想到了RecyclerView,但我们常用的RecyclerView是不支持无限循环滚动的,
- RabbitMQ的一些基本组件Producer:消息的生产者Consumer:消息的消费者Broker:MQ服务器,管理队列、消息Messa
- @JsonFormat处理LocalDateTime失效Failed to convert property value of type &
- 先来看看效果:一、添加依赖库的步骤1.项目的gradle文件内的做以下改动allprojects { repositories
- springboot环境切换失效概述最近在使用-Dspring.profiles.active=te 来切换spring-boot的环境时,
- 本文实例为大家分享了Android实现层叠卡片式banner的具体代码,供大家参考,具体内容如下效果图如下:背景由于公司VIP模块项目需要,
- 递归方法定义本身调用方法本身的现象叫做递归在这之前我们学的东西:例如StringBuffer.append().append().appen
- 一、什么是反射机制 简单的来说,反射机制指的是程序在运行时能够获取自身的信息。在java中,只要给定类的名字,那么就可以通过反射机制来获得类
- 简单基础的定制android系统,使第一次开机就安装系统应用和预装应用,加载服务工具和其它文件,开机启动画面,设置默认输入法、屏幕亮度等等。
- 今天和大家聊一聊Android中关于FontMetrics的几个属性的理解,在Android中用画笔绘制文字时,文字最终的大小是和绘制文字的
- 本文实例讲述了Android编程开发之TextView单击链接弹出Activity的方法。分享给大家供大家参考,具体如下:话不多说直接上码:
- 一、点睛邻接矩阵通常采用一个一维数组存储图中节点的信息,采用一个二维数组存储图中节点之间的邻接关系。邻接矩阵可以用来表示无向图、有向图和网。
- 这篇文章主要介绍了JavaMail与Spring整合过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需
- 前文传送门:异线程下回收对象获取异线程释放对象上一小节分析了异线程回收对象, 原理是通过与stack关联的WeakOrderQueue进行回
- 目录一、 * 简介二、 * 的多种实现1. 基于JDK的实现2. 基于cglib的实现三、为什么要有基于cglib的实现四、两种方式的适