如何使用 spring 数据 jpa 处理缓存实体的更新?

How to handle updates on cached entites with spring data jpa?

我在大多数 spring 数据存储库上使用 springs @Cacheable 注释。这包括 findById(...) 方法。结果每次我想编辑和保存一个实体时,我都会得到一个异常:

org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)

这是我的存储库:

public interface ProductRepository extends Repository<Product, Long> {

    @Cacheable(cacheNames = "products", key = "#id")
    Optional<Product> findById(Long id);

    @Caching(
        put = {
            @CachePut(cacheNames = "products", key = "#result.id"),
            @CachePut(cacheNames = "productByIsbn", key = "#entity.isbn", condition = "#entity.isbn != null")
        })
    <S extends Product> S save(S entity);

    @Override
    @Caching(evict = {
        @CacheEvict(cacheNames = "products", key = "#entity.id"),
        @CacheEvict(cacheNames = "productByIsbn", key = "#entity.isbn", condition = "#entity.isbn != null")
    })
    void delete(AbstractProductEntity entity);

    @Cacheable(cacheNames = "productByIsbn", condition = "#isbn != null")
    Optional<Product> findOneByIsbn(String isbn);
}

据我了解,我的缓存实体与持久化上下文分离,无法再次保存。

解决这个问题的最佳方法是什么?

我知道我可以添加第二个 findByUncached 方法,该方法仅在内部使用并绕过缓存。但对我来说,这似乎是一个糟糕的解决方案,因为我必须对代码段中使用的所有方法执行此操作,其中实体从存储库中读取,然后再次持久化。

如果我相信我的缓存是最新的,我可以重新附加缓存的对象,但我如何使用 spring-data 来做到这一点?普通版本库界面没有merge方法。

EDIT1:感谢 Jens Schauder 指出我的版本问题。

结果是所有缓存实体中的版本 属性 在调用 save(...) 后未在缓存中正确更新。虽然我不完全确定为什么会这样,但是用 @CacheEvict 替换 @CachePut 注释并将缓存更新留给下一次读取解决了这个问题。我现在正在研究 @CachePut 应该如何工作。

这里的问题不在于实体分离。 Spring 如果您为分离的实体调用 save,Data JPA 将执行合并。 问题是该实体是“陈旧的”,即它的版本属性不再与数据库中的版本匹配,因为其他一些事务更新了该实体,从而也更新了版本。

一般来说,我建议不要将第三方缓存解决方案与 JPA 一起使用。 JPA 有自己的两个缓存层。 一级缓存是 EntityManager 本身。 对您来说更有趣的可能是 2nd level cache and possibly the query cache,它是 Hibernate 特定的 afaik。