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种情况:

  1. 使用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

回到顶部