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设置锁的级别即可.
二 乐观锁的详细用法
乐观锁本篇的主要内容
实体类是关键 , 乐观锁常用方法是通过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,这种是手动的提交方法
以上为个人经验,希望能给大家一个参考,也希望大家多多支持自由互联。
【文章出处:台湾服务器 转载请保留连接】