0

我有一个Entity简化的,看起来像这样:

@Entity
@Table(name = "SOMETABLE")
public class SomeEntity {

    // real code has id and more columns

    @Column(name = "SOMECOLUMN")
    private String someColumn;

    @Transient
    private SomeObject transientObject;

    // getters and setters
}

@NamedQueryDAO 方法通过使用 a和 JPA EntityManager(大致存根)加载实体列表:

@Transactional
public List<SomeEntity> getSomeEntities() {
    TypedQuery<SomeEntity> query = entityManager.createNamedQuery("findSomeEntities", SomeEntity.class);
    List<SomeEntity> someEntities = query.getResultList();
    for (SomeEntity someEntity : someEntities) {
        someEntity.setTransientObject(<some value here>);
        return someEntities;
    }
}

请注意,此方法还设置transientObject(在代码示例中进行了简化)。

下次getSomeEntities()调用时,query.getResultList();返回transientObject仍然设置的对象列表。我希望瞬态对象为空,但事实并非如此。没有启用一级或二级缓存。

为了进一步混淆这一点,这只发生在单元测试期间,我们使用 HSQL 内存数据库。在 Tomcat 服务器上运行 Web 应用程序时,它工作正常。

我调试了一下,我发现在运行单元测试时,会话缓存(我理解它总是为 Hibernate 启用)似乎加载了所有以前加载的对象,但在应用程序服务器上运行时它是空的。我怀疑这意味着休眠从缓存而不是数据库中获取对象。

另外值得一提的是它是一个 Spring 应用程序。

这是什么原因?或者改写我的主要问题:为什么第二次使用 HSQLDB 加载实体时瞬态对象不为空?

4

3 回答 3

0

你在你的测试用例上使用@Transactional 吗?如果是,请尝试删除它。

于 2013-03-20T00:06:49.577 回答
0

听起来像是与一级缓存/会话缓存有关。

第一级缓存存储会话上下文中的所有对象并重用它。这在您使用应用程序服务器时不会产生影响,因为每个事务都会启动一个新会话。

换句话说,每次调用 DAO 方法都会导致创建一个新会话,这意味着缓存是空的。

要解决此问题,请尝试在第二次通话之前关闭会话并创建一个新会话。

您也可以尝试将它包含在两个不同的事务中。这也可能让它发挥作用。

编辑:回答马格努斯的问题

我真的应该换一种说法。每个休眠会话将在事务结束时关闭。

根据Session 的休眠文档

Session 的生命周期受逻辑事务的开始和结束的限制。(长事务可能跨越多个数据库事务。)

从应用服务器的角度来看,在绝大多数情况下(可能是为了实用的所有情况),它无法识别包含多个物理事务的逻辑事务。

因此,它将每个物理事务视为逻辑事务。这意味着会话在每个物理事务结束时关闭。

Seam's Conversation Scope 和 Java EE 6 中的新 Scopes的上下文中,@ViewScoped有争议的是,可以识别跨越多个物理事务的逻辑事务。但是,我不认为它那么简单,也不相信它是以这种方式实现的。但是,无论哪种方式,我都没有任何信息可以证实这一点。

于 2013-03-19T20:12:46.483 回答
0

这是 Hibernate 一级缓存的效果——它始终处于开启状态。一级缓存提供了这种行为:

如果您从同一个 Session 中获取同一行两次,您会从 Session 对象中获得相同的实体实例(即,具有 == 语义)。

听起来您在测试时对 getSomeEntities 的多次调用具有相同的会话 - 但在运行时没有。这让我认为您的测试用例的处理方式与您的应用程序代码不同。

于 2013-09-13T12:08:51.370 回答