概述
一、事件原因。
在使用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值并没有改变。
五、先更新,后查询。会查询出更新后的数据。
//把名字更新为 “myUpdate周4”
organizationRepo.myUpdate(1803L);
Organization organization = organizationRepo.findById(1803L).get();
//“myUpdate周4”
System.out.println(organization.getName());
可以看到最后打印出myUpdate周4表示查询出了更新后的数据。hibernate生成语句的顺序如下:
update Organization。
select from Organization。
select from Organization。
六、 高并发陷进。
如果我们要写一个方法methodA(),目的是减掉某个商品数量amount。就会有2种情况:
使用save()语句更新。如下:
public void methodA() {
Goods goods = goodsRepo.findById(1);
goods.setAmount(goods.getAmount() - quantity);
goodsRepo.save(goods);
}
这个时候缓存也被更新。同一个事务中,外部methodB()调用methodA()之后,在methodB()中使用goodsRepo.findById(1)查询库存。能保证查询的缓存数量和数据库一致。
但是高并发下,如果期间有另一个线程把商品的Amount更新为0,当前线程执行完之后商品数和当前线程一样,就导致其他线程的更新丢失。
2. 使用自定义语句更新amount。如下:
检查了数量的自定义update语句。
@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);
减数量的逻辑。
public void methodA() {
Goods goods = goodsRepo.findById(1);
//减数量
goodsRepo.myUpdate(1,amount);
}
这个时候。同一个事务中,外部methodB()调用methodA()之后,在methodB()中使用goodsRepo.findById(1)查询库存。就会出现和预期不一致的情况。
总结: 必须让预期的数据库数量,和缓存的数量一致。
最后
以上就是活力纸飞机为你收集整理的jpa删除数据后数据库无修改_Jpa同一个事务中对同一数据先改后查,获取数据并未改变。...的全部内容,希望文章能够帮你解决jpa删除数据后数据库无修改_Jpa同一个事务中对同一数据先改后查,获取数据并未改变。...所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复