Hibernate 查询缓存 LazyInitializationException

问题描述 投票:0回答:3

我启用了 L2 和查询缓存,当我有一个已缓存的查询时,我遇到了一个奇怪的问题。实体中的所有关系都是惰性初始化的。这是我正在查询的实体的示例:

@Entity
@Cache(usage = READ_WRITE)
@Data
@NoArgsConstructor
@Accessors
@EqualsAndHashCode(of = "id", callSuper = false)
public class TestEntity {

  /** The Constant serialVersionUID. */
  private static final long serialVersionUID = 1L;

  @Id
  @Column(updatable = false)
  private Long id;

  @OneToOne(cascade = ALL, fetch = LAZY)
  private AnotherTestEntity anotherTestEntity;

}

@Entity
@Cache(usage = READ_WRITE)
@Data
@NoArgsConstructor
@Accessors
@EqualsAndHashCode(of = "id", callSuper = false)
public class AnotherTestEntity {

  /** The Constant serialVersionUID. */
  private static final long serialVersionUID = 1L;

  @Id
  @Column(updatable = false)
  private Long id;

  @Column
  private String property;

}

当我执行未缓存的查询时:

@Transactional(readOnly = true)
public TestEntity findTestEntity() {
  TestEntity testEntity = testEntityRepository.findOne(1);
  testEntity.getAnotherTestEntity().getProperty(); 

  return testEntity;
}

我第一次调用此方法时,它会查询数据库并将实体添加到二级缓存中。我第二次调用它时,它从 L2 缓存加载实体,并且仍然工作正常。

当我调用缓存的查询时,问题就出现了。这是一个例子:

@Repository
public interface TestEntityRepository {

  @Cachable(cacheNames = "testQuery")
  TestEntity findOne(Long id);
}

我会用同样的方法:

@Transactional(readOnly = true)
public TestEntity findTestEntity() {
  TestEntity testEntity = testEntityRepository.findOne(1);
  testEntity.getAnotherTestEntity().getProperty(); 

  return testEntity;
}

当我第一次调用它时,它仍然可以正常工作 - 从数据库加载数据。当第二次调用使用查询缓存时,问题就出现了。当我访问惰性初始化关系时抛出此异常:

Caused by: org.hibernate.LazyInitializationException: could not initialize proxy - no Session

我可以看到延迟初始化实体的会话为空,但我不明白为什么会发生这种情况。正如我们所知,查询缓存仅包含与该查询关联的实体的 id,然后它会从 L2 检索它们(参考:https://dzone.com/articles/pitfalls-hibernate-second-0)。所以我不明白为什么第一个例子(没有查询缓存)工作正常,而第二个例子却表现得如此奇怪。有人可以解释并告诉我我做错了什么吗?

hibernate spring-boot spring-data
3个回答
3
投票

所以我只是深入研究这个问题,结果发现 Spring 缓存抽象不能与 hibernate 延迟加载代理一起使用。 Spring 为您提供了一个抽象,而他们不了解 hibernate 和 hazelcast。然后 hazelcast 提供了与 spring 一起使用的实现。因此,当调用带有 @Cachable 注释的方法时,Spring 方面会检查缓存(使用提供的 CacheManager - 在本例中为 HazelcastCacheManager)并检索缓存中的内容。这里的问题是 hibernate 代理中的会话是瞬态的(这种情况绝对正常),因此我们从缓存中检索一个没有 hibernate 会话的实体,并且由于 spring 不想与 hibernate 耦合,因此会话是未设置。然后我们收到 LazyInitializationException。但归根结底,这是一个非常常见的问题,奇怪的是到了春天还没有解决方案。有效的方法是使用 hibernate 查询缓存,但使用 hazelcast 还存在其他一些缺点。


0
投票

这可能是一个错误,但可以肯定的是,您需要使用此测试用例模板将其复制到最新的 Hibernate ORM。

如果无法重现,则意味着问题已修复,需要升级 Hibernate,或者问题源于 Spring,而不是 Hibernate。


0
投票

根据我的说法,最初您正在发送一个 get 请求,并且缓存正在存储数据,以便它可以在第二次使用相同的 Id 调用 get 方法时直接检索数据。 现在发生的情况是,您有一个延迟加载的实体,在您第一次发出 get 请求时,其数据未由缓存存储,现在第二次缓存检索延迟加载的实体数据,但因为不存在数据且没有活动会话是否从数据库中获取该实体,它抛出一个 LazyInitialization 异常。

如何解决: 1->使用fetch=FetchType.EAGER加载 2-> 不要发送这个延迟加载的实体作为响应 3-> 使用 Dto 对象并返回该对象而不是您的实体,您可以轻松地在 Web 中了解它,使用 dto 避免了很多异常。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.