JPA 加锁机制及@Version版本控制方式

作者:black-ant 时间:2022-10-06 10:57:58 

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

标签:JPA,加锁,@Version
0
投稿

猜你喜欢

  • unity实现场景跳转

    2023-08-30 22:58:15
  • Java 面试题和答案 -(上)

    2023-10-08 08:15:56
  • java compare compareTo方法区别详解

    2022-06-26 08:13:55
  • 四步轻松搞定java web每天定时执行任务

    2022-03-31 20:57:08
  • C#几种截取字符串的方法小结

    2023-07-16 09:55:10
  • 一篇文章带你了解XGBoost算法

    2021-10-10 01:14:45
  • Java编程复用类代码详解

    2021-09-13 10:06:34
  • 详谈Springfox与swagger的整合使用

    2021-11-25 21:08:34
  • IDEA的Swing可视化插件JFormDesigner详解

    2023-09-23 08:02:54
  • java判断字符串String是否为空问题浅析

    2023-08-25 07:06:06
  • 第一次使用Android Studio时你应该知道的一切配置(推荐)

    2022-01-08 00:49:38
  • Spring Boot CLI使用教程

    2023-03-30 03:37:02
  • 全面解析Java支持的数据类型及Java的常量和变量类型

    2022-03-25 16:54:10
  • Java多线程之Future设计模式

    2022-07-19 05:28:25
  • C#中将xml文件反序列化为实例时采用基类还是派生类的知识点讨论

    2022-08-04 19:27:32
  • 分享C#操作内存读写方法的主要实现代码

    2022-10-08 08:08:44
  • 解决IDEA克隆代码后在右下角没有git分支的问题

    2021-11-23 09:43:19
  • C#使⽤XmlReader和XmlWriter操作XML⽂件

    2023-12-13 10:25:00
  • Spring Boot 配置和使用多线程池的实现

    2022-09-04 19:53:02
  • 分析并发编程之LongAdder原理

    2023-05-11 17:19:30
  • asp之家 软件编程 m.aspxhome.com