JPA的加锁机制有两种,乐观锁和悲观锁。
乐观锁:
乐观锁的特点在于认为数据冲突或者更新丢失等情况是很少发生的.当发生的时候,抛出异常和回滚就足够解决问题.
悲观锁:
悲观锁的逻辑在于认为每次数据操作都很有可能发生冲突,所以一开始就获得记录的锁,再进行记录的操作是解决问题的优先选择.
一 简述悲观锁的用法
悲观锁通常是SQL级别的,通过读写时先拿到锁实现,在SQL语句中就会有体现.
1.1 EntityManager 用法
1
2
3
4
|
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注解)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
@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将线程沉睡,测试事务的提交性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
@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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
@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是自动增长的,唯一的缺点是抛出的异常不易捕获,捕获的方法:
1
2
3
4
5
6
7
8
9
10
11
12
|
@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/zzg19950824/article/details/85468318