【问题标题】:How to handle updates on cached entites with spring data jpa?如何使用 spring data jpa 处理缓存实体的更新?
【发布时间】:2020-11-16 23:59:30
【问题描述】:

我在我的大部分 spring-data 存储库中使用 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 来做到这一点?普通仓库接口上没有合并方法。

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

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

【问题讨论】:

    标签: spring spring-data-jpa spring-data spring-cache


    【解决方案1】:

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

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

    【讨论】:

    • 我不确定为什么人们一直建议只使用 jpa。他们可以携手合作,您如何为 Id 以外的实体使用不同的密钥?例如@Cacheable(key = "#name") Optional&lt;Obj&gt; findByName(String name);
    猜你喜欢
    • 2012-08-06
    • 1970-01-01
    • 1970-01-01
    • 2018-04-20
    • 1970-01-01
    • 2013-05-09
    • 1970-01-01
    • 2019-09-21
    • 1970-01-01
    相关资源
    最近更新 更多