我启用了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)。所以我无法理解为什么第一个例子(没有查询缓存)工作正常,第二个例子表现得如此奇怪。有人可以解释并告诉我我做错了什么吗?
答案 0 :(得分:0)
这可能是一个错误,但可以肯定的是,您需要使用this test case template针对最新的Hibernate ORM复制它。
如果你无法重现它,那就意味着问题已解决,你需要升级Hibernate,或者问题源于Spring,而不是Hibernate。
答案 1 :(得分:0)
所以我只是深入探讨了这个问题,事实证明,Spring缓存抽象不能与休眠的延迟加载代理一起使用。 Spring为您提供了一个抽象,他们不了解休眠和榛子广播。然后hazelcast提供了它们的实现以与Spring一起工作。因此,当调用带有@Cachable注释的方法时,Spring方面会检查缓存(使用提供的CacheManager,在这种情况下为HazelcastCacheManager),并检索缓存中的内容。这里的问题是,休眠代理中的会话是瞬态的(这样是绝对正常的),因此我们从缓存中检索了没有休眠会话的实体,并且由于spring不想与休眠耦合,因此该会话是没有设置。然后,我们收到LazyInitializationException。但归根结底,这是一个非常普遍的问题,奇怪的是,到春天还没有解决方案。起作用的是使用休眠查询缓存,而使用hazelcast则还有其他缺点。