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
0
投稿
猜你喜欢
- LRU:Least Recently Used最近最少使用,当缓存容量不足时,先淘汰最近最少使用的数据。就像JVM垃圾回收一样,希望将存活的
- 本文实例讲述了Java基于socket实现简易聊天室的方法。分享给大家供大家参考。具体实现方法如下:chatroomdemo.javapac
- 线性表是其组成元素间具有线性关系的一种数据结构,对线性表的基本操作主要有,获取元素,设置元素值,遍历,插入,删除,查找,替换,排序等。而线性
- 我计划在后续的一段时间内,写一系列关于java 9的文章,虽然java 9 不像Java 8或者Java 11那样的核心java版本,但是还
- 本文实例讲述了C#后台创建控件并获取值的方法。分享给大家供大家参考。具体实现方法如下:前台代码:<form id="form
- 提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档前言这两天在项目中使用到Java的导入导出功能,以前对这块有一定了解,但是没
- Java事件处理机制和适配器最重要的是理解事件源,监视器,处理事件的接口的概念。1.事件源:是能够产生时间的对象都可以叫事件源,比如文本框,
- 背景在我们实际生产容器化部署过程中,往往会遇到 Docker 镜像很大,部署发布很慢的情况影响 docker 镜像大小的因素,主要有以下三个
- producer是生产者的意思:指生产数据的线程,consumer是消费者的意思:指的是使用数据的线程public class Produc
- execution (常用,方法级别的匹配)语法:execution(modifiers-pattern? ret-type-pattern
- 今天在群里看见有人问了这个问题,那就把我自己总结的知识拿出来与大家分享一下吧..当然可能还有什么不对的地方,希望指出:***msbase.j
- 1. 判断允许上传文件的 文件后缀/图片后缀/相片后缀 和 其它工具类import org.springframework.stereoty
- 面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式。数组虽然也可以存
- 前沿知识ThreadLocal存储线程变量,使用set方法设置变量,使用get方法获取变量线程隔离的实现是每个Thread类有一个类型为Th
- 本文实例讲述了C#实现软件监控外部程序运行状态的方法。分享给大家供大家参考。具体方法如下:需要 * 一个程序,用于监控另一个程序运行状态,一旦
- SpringBoot读取外置logback配置文件springboot项目可以读取外置配置文件,避免了修改配置文件需要重新打包部署的问题。部
- Java的一个重要特性就是通过垃圾收集器(GC)自动管理内存的回收,而不需要程序员自己来释放内存。理论上Java中所有不会再被利用的对象所占
- 谈到多线程就不得不谈到Synchronized,重要性不言而喻,今天主要谈谈Synchronized的实现原理。Synchronizedsy
- Spring Boot 解决富文本上传图片跨域在前后端分离的情况下,后台所写的接口在前端调用的时候,可能前端浏览器已经读取到了数据,但是在前
- 不知道大家对千篇一律的404 Not Found的错误页面是否感到腻歪了?其实通过很简单的配置就能够让Spring MVC显示您自定义的40