Jpa同一个事务中对同一数据先改后查,获取数据并未改变。
一、事件原因。
在使用JPA的时候。在同一个事务中,对某条数据。先查询,然后更新,再查询
。由于第2次查询是从缓存取得第1次查询的结果。会出现2种情况:
- 如果更新是用save()等JPA自带的语句。会更新缓存。第2次查询的和数据库一致。
- 如果用自己写的更新语句。因为没有更新缓存。第2次查询从缓存获取数据,导致更新的数据第2次取不到。导致查询数据和数据库预期的不一致。
本质上就是要清楚,在同一个事务中,事务没有提交。第2次查询从缓存获取数据。
二、 证明第二次从缓存获取数据。
上面的代码中,2个对象的地址都一样是Organization@9556
。所以第2次查询是从缓存获取的数据。
三、 参考例子1:
@Transactional @Override
public void update() {
//查询
Organization organization = organizationRepo.findById(1803L).get();
organization.setName("Test step1ppp33");
//更新(因为是在事务中,所以更新了缓存,但是没有保存到数据库)
organizationRepo.save(organization);
////查询数据
Organization org2 = organizationRepo.findById(1803L).get();
//获得的数据是 org2.getName()===Test step1ppp33 。证明没有从数据库拿,是从同一个事务缓存中取得的。
System.out.println("org2.getName()==="+org2.getName());
}
上面代码中org2.getName()===Test step1ppp33
。证明没有从数据库拿,是从同一个事务缓存中取得的。
四、 参考例子2:
@Transactional @Override
public void update() {
//查询
Organization organization = organizationRepo.findById(1803L).get();
/**
* @Modifying
* @Query("UPDATE Organization o set o.name="myUpdate" WHERE o.id=:id")
*void myUpdate(@Param("id") Long id);
*/
// 只做了一个更新语句,没有更新缓存的organization变量。
organizationRepo.myUpdate(1803L);
//查询缓存数据
Organization org2 = organizationRepo.findById(1803L).get();
//org2.getName()===Test step1ppp33 ,证明从缓存获取的数据。更新的数据没有被查询到,
System.out.println("org2.getName()==="+org2.getName());
}
自己写了一个update语句,并没有更新缓存的数据,所以第2次查询出来的name
值并没有改变。
五、 高并发陷进。
如果我们要写一个方法methodA()
,目的是减掉某个商品数量amount,首先查询一条商品数据。然后减amount,就会有2种情况:
- 使用save()语句更新。如下:
Goods goods = goodsRepo.findById(1);goods.setAmount(goods.getAmount() - quantity);
goodsRepo.save(goods);
这个时候缓存也被更新。外部methodB()
调用methodA()
之后,在methodB()
中使用goodsRepo.findById(1)
查询库存。能保证查询的缓存数量和数据库一致。
但是高并发下,如果期间有另一个线程把商品的Amount
更新为0,当前线程执行完之后商品数和当前线程一样,就导致其他线程的更新丢失。
2. 使用自定义语句更新。如下:
检查了数量的自定义语句。
@Modifying @Query("UPDATE Goods g SET g.amount=g.amount - :amount WHERE g.id=:id AND g.amount >=:amount")
void myUpdate(@Param("id") Long id,@Param("amount") Long amount);
减数量的逻辑。
Goods goods = goodsRepo.findById(1);//减数量
goodsRepo.myUpdate(1,amount);
这个时候,goodsRepo.findById(1)
查询出的goods
对象,缓存并没有被更新。methodB()
调用methodA()
之后,在methodB()
中使用goodsRepo.findById(1)
查询库存。就会出现和预期不一致的情况。
总结: 必须让预期的数据库数量,和缓存的数量一致。
以上是 Jpa同一个事务中对同一数据先改后查,获取数据并未改变。 的全部内容, 来源链接: utcz.com/z/510736.html