Hibernate查询缓存LazyInitializationException

时间:2018-06-08 07:38:21

标签: hibernate spring-boot spring-data

我启用了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缓存中添加实体。我第二次调用它时,它从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)。所以我无法理解为什么第一个例子(没有查询缓存)工作正常,第二个例子表现得如此奇怪。有人可以解释并告诉我我做错了什么吗?

2 个答案:

答案 0 :(得分:0)

这可能是一个错误,但可以肯定的是,您需要使用this test case template针对最新的Hibernate ORM复制它。

如果你无法重现它,那就意味着问题已解决,你需要升级Hibernate,或者问题源于Spring,而不是Hibernate。

答案 1 :(得分:0)

所以我只是深入探讨了这个问题,事实证明,Spring缓存抽象不能与休眠的延迟加载代理一起使用。 Spring为您提供了一个抽象,他们不了解休眠和榛子广播。然后hazelcast提供了它们的实现以与Spring一起工作。因此,当调用带有@Cachable注释的方法时,Spring方面会检查缓存(使用提供的CacheManager,在这种情况下为HazelcastCacheManager),并检索缓存中的内容。这里的问题是,休眠代理中的会话是瞬态的(这样是绝对正常的),因此我们从缓存中检索了没有休眠会话的实体,并且由于spring不想与休眠耦合,因此该会话是没有设置。然后,我们收到LazyInitializationException。但归根结底,这是一个非常普遍的问题,奇怪的是,到春天还没有解决方案。起作用的是使用休眠查询缓存,而使用hazelcast则还有其他缺点。