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,这种是手动的提交方法

参考

https://blog.csdn.net/taiyangdao/article/details/51989440

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值