评论

收藏

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

编程语言 编程语言 发布于:2022-02-21 15:27 | 阅读数:666 | 评论:0

这篇文章主要介绍了JPA 加锁机制及@Version版本控制方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
目录

  • 一 简述悲观锁的用法

    • 1.1 EntityManager 用法

  • 二 乐观锁的详细用法

    • 实体类(注意其中的@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事务开始即设置versionLockMode.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);
}
总结
使用很简单,version是自动增长的,唯一的缺点是抛出的异常不易捕获,捕获的方法:
@Resource
private UserTransaction rtc;
 try {
    rtc.begin();
    User user = userService.findUser("gang");
    user .setDesc("异常捕获");
     rtc.commit();
  } catch (OptimisticLockException e) {
    throw new OptimisticLockException ();
  } catch (Exception e) {
    throw new Exception ();
  }
注意其中的 rtc.begin(); 以及 rtc.commit();
不同于@Transaction,这种是手动的提交方法
以上为个人经验,希望能给大家一个参考,也希望大家多多支持CodeAE代码之家
原文链接:https://blog.csdn.net/zzg19950824/article/details/85468318

关注下面的标签,发现更多相似文章