Jpa缓存问题

记一次jpa缓存引起的数据不一致问题

问题描述:有一个查询数据字典类型的接口,此接口功能为根据数据字典类型ID,查询数据字典类型及其下面所有的数据字典。

一.重现问题

  1. 首先我们利用swagger请求此接口

这是该接口的service层

@Transactional(readOnly = true)
public Optional<DataDictionaryTypeVo> findByTypeId(Long typeId) {
    return dataDictionaryTypeRepository.findById(typeId)
        .map(dataDictionaryTypeVoMapper::toDto);
}

在这里插入图片描述

得到的相应json

{
  "requestId": "5049dca4-3171-4e6a-be88-7a6dae635b77",
  "success": true,
  "data": {
    "createdBy": "system",
    "createdDate": "2019-09-09T03:38:38Z",
    "lastModifiedBy": "system",
    "lastModifiedDate": "2019-09-09T03:38:38Z",
    "id": 152,
    "name": "性别",
    "dataDictionaries": [
      {
        "createdBy": "admin",
        "createdDate": "2020-07-23T06:36:56Z",
        "lastModifiedBy": "admin",
        "lastModifiedDate": "2020-07-23T06:36:56Z",
        "id": 37,
        "name": "男",
        "code": null,
        "disabled": false,
        "desc": null,
        "level": 1,
        "parentId": null,
        "typeId": 152,
        "typeName": "性别",
        "children": []
      },
      {
        "createdBy": "admin",
        "createdDate": "2020-07-23T06:37:33Z",
        "lastModifiedBy": "admin",
        "lastModifiedDate": "2020-07-23T06:37:33Z",
        "id": 38,
        "name": "女",
        "code": null,
        "disabled": false,
        "desc": null,
        "level": 1,
        "parentId": null,
        "typeId": 152,
        "typeName": "性别",
        "children": []
      }
    ]
  }
  ...
  ...
}

可以看出,该接口正常得到数据字典类型和数据字典

  1. 添加一个类型为性别的数据字典
    在这里插入图片描述

  2. 添加成功后,再次请求1步骤中的接口

重点来了,此时得到的还是1中的结果,性别数据字典类型中没有查出刚添加的不详

为了验证数据库中是否已经添加了改数据字典,我们请求另一接口根据数据字典类型查询数据字典

在这里插入图片描述

可以看出能查出2步骤添加的内容

二、发现并解决问题

经过查资料看日志,得到了jpa查询的话某些情况下会直接查询缓存的结论。我们“1”步骤中的那个接口是根据数据字典类型id,查询数据字典类型,并且带出数据字典类型下的所有数据字典类型,这种情况下如果没有在添加或者删除数据字典的时候操作jpa缓存,就会造成数据不一致的情况。当然我们也可以在日志中看到

在这里插入图片描述

解读:黄色框框起来的是步骤"2"添加成功的日志,下面红色框框起来的是步骤"3"中请求数据的接口,可以看出来,这边并没有打印sql语句,我们可以与下面的图对比一下

我们先来看下数据字典添加内容的service

public DataDictionaryDTO save(DataDictionaryDTO dataDictionaryDTO) throws Exception {
    DataDictionary dataDictionary = dataDictionaryMapper.toEntity(dataDictionaryDTO);
    dataDictionary = dataDictionaryRepository.save(dataDictionary);
    DataDictionaryDTO result = dataDictionaryMapper.toDto(dataDictionary);
    return result;
}

这种写法是没有操作缓存的,我们会遇到数据不一致的情况,接下来贴出符合我们业务场景的代码

public DataDictionaryDTO save(DataDictionaryDTO dataDictionaryDTO) throws Exception {
    DataDictionary dataDictionary = dataDictionaryMapper.toEntity(dataDictionaryDTO);
    dataDictionary = dataDictionaryRepository.save(dataDictionary);
    DataDictionaryDTO result = dataDictionaryMapper.toDto(dataDictionary);
    // 添加的情况,修改不需要进入if
    if (dataDictionaryDTO.getId() == null) {
        DataDictionary finalDataDictionary = dataDictionary;
        Optional.ofNullable(dataDictionaryDTO.getTypeId())
            .flatMap(dataDictionaryTypeRepository::findById)
            .ifPresent(type -> type.addDataDictionary(finalDataDictionary));
    }
    return result;
}

这边贴一下DataDictionaryType类

public class DataDictionaryType{
   	...
    ...
        
    @OneToMany(mappedBy = "type")
    @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
    private Set<DataDictionary> dataDictionaries = new HashSet<>();

   	...
    ...
        
    public DataDictionaryType addDataDictionary(DataDictionary dataDictionary) {
        this.dataDictionaries.add(dataDictionary);
        dataDictionary.setType(this);
        return this;
    }

    public DataDictionaryType removeDataDictionary(DataDictionary dataDictionary) {
        this.dataDictionaries.remove(dataDictionary);
        dataDictionary.setType(null);
        return this;
    }

   	...
    ...
}

这样我们就能正常使用符合我们业务场景的接口了

在这里插入图片描述

与上面的比较,可以把sql日志打印了,说明没有走jpa缓存

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值