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

 更新时间:2021年10月15日 09:52:25   作者:black-ant  
这篇文章主要介绍了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);
}

总结

使用很简单,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,这种是手动的提交方法

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 在Mybatis使用Integer与''进行比较的坑及解决

    在Mybatis使用Integer与''进行比较的坑及解决

    这篇文章主要介绍了在Mybatis使用Integer与''进行比较的坑及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • springboot整合消息队列RabbitMQ

    springboot整合消息队列RabbitMQ

    这篇文章主要介绍了springboot整合消息队列RabbitMQ,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-08-08
  • Java访问Hadoop分布式文件系统HDFS的配置说明

    Java访问Hadoop分布式文件系统HDFS的配置说明

    Hadoop的能提供高吞吐量的数据访问,是集群式服务器的上的数据操作利器,这里就来为大家分享Java访问Hadoop分布式文件系统HDFS的配置说明:
    2016-06-06
  • SpringBoot接入支付宝支付的方法步骤

    SpringBoot接入支付宝支付的方法步骤

    这篇文章主要介绍了SpringBoot接入支付宝支付的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • SpringTask-Timer实现定时任务的详细代码

    SpringTask-Timer实现定时任务的详细代码

    在项目中开发定时任务应该一种比较常见的需求,今天通过示例代码给大家讲解SpringTask-Timer实现定时任务的相关知识,感兴趣的朋友一起看看吧
    2024-06-06
  • springboot整合SSE技术开发小结

    springboot整合SSE技术开发小结

    本文主要介绍了springboot整合SSE技术开发小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-11-11
  • java实现的MD5摘要算法完整实例

    java实现的MD5摘要算法完整实例

    这篇文章主要介绍了java实现的MD5摘要算法,结合完整实例形式分析了java实现md5单项加密的具体步骤与相关操作技巧,需要的朋友可以参考下
    2017-01-01
  • Spring Boot实战之数据库操作的示例代码

    Spring Boot实战之数据库操作的示例代码

    本篇文章主要介绍了Spring Boot实战之数据库操作的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • Spring使用AOP完成统一结果封装实例demo

    Spring使用AOP完成统一结果封装实例demo

    这篇文章主要介绍了Spring使用AOP完成统一结果封装,本文通过实现demo给大家详细讲解,代码简单易懂,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-02-02
  • 使用SpringBoot创建一个RESTful API的详细步骤

    使用SpringBoot创建一个RESTful API的详细步骤

    使用 Java 的 Spring Boot 创建 RESTful API 可以满足多种开发场景,它提供了快速开发、易于配置、可扩展、可维护的优点,尤其适合现代软件开发的需求,帮助你快速构建出高性能的后端服务,需要的朋友可以参考下
    2025-01-01

最新评论