2013-03-19 64 views
0

加载实体第二遍的时候我有一个Entity的是,简化,看起来是这样的:瞬态场不空使用HSQLDB

@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 
} 

一个DAO方法,通过使用@NamedQuery和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启用)似乎在运行单元测试时,装载所有以前加载的对象,但在运行时,它是空的应用程序服务器。我怀疑这意味着hibernate会从缓存而不是数据库中获取对象。

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

这是什么原因?或更改我的主要问题:当使用HSQLDB第二次加载实体时,为什么transient对象不为null?

+0

所以在你调用'someEntity.setTransientObject()之前,'',someEntity.transientObject!= null'? – 2013-03-19 19:55:07

+0

我第一次打电话给方法,是的。第二次,它是**不**空。是的,它是**之前**我再次设置它。试图澄清这个问题。 – Magnilex 2013-03-19 19:57:21

回答

0

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

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

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

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

你也可以尝试在两个不同的交易中包含它。这也可能让它起作用。

编辑:从马格努斯

我真的应该已经把它的另一种方式回答问题。每个休眠会话将在事务结束时关闭。

按照​​,

会话的生命周期是由一个逻辑事务的开始和结束边界。 (长事务可能跨越多个数据库事务。)

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

由于这个原因,它将每个物理事务视为逻辑事务。这意味着会话在每次实际交易结束时都会关闭。

Seam的对话范围和Java EE 6中的新范围(如@ViewScoped)中,有争议的是可以识别跨越多个物理事务的逻辑事务。但是,我认为这不是那么简单,不相信它是以这种方式实施的。但是,我无法确认这一点。

+0

'这对您使用应用程序服务器没有影响,因为每个事务都会启动一个新会话。“为什么会这样?我可以通过在我的测试中调用'entityManager.clear()'来实际摆脱这个问题,但那不是我想在那里做的事情。 DAO方法是事务性的,我也证实它实际上是两个不同的事务。 – Magnilex 2013-03-19 20:38:11

+0

@MagnusTengdahl - 编辑答案,包括你的问题的答案。此外,清除entitymanager可能更有效,创建一个新的会话来模拟App Server – 2013-03-20 09:24:35

+0

感谢您的努力。然而,我仍然不完全清楚。 :)你给我的建议是在我的单元测试中实际调用'.clear()'来确保会话缓存是空的?这真的是我想避免的,这太容易出错了。为什么Hibernate/HSQL不能处理这个问题,因为DAO方法实际上封装在一个事务中?你可能回答了这个问题,但我没有从你的答案中得到答案。 – Magnilex 2013-03-20 09:45:11

0

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

+0

没有。我的测试正在做他们应该做的。 – Magnilex 2013-03-20 08:10:39

0

这是Hibernate一级缓存的效果 - 这是总是上。第一级缓存提供了这种行为:

如果从同一会话获取相同的行两次,你会得到相同的实体实例(即用==语义)从Session对象。

这听起来像你有同样的会话在测试时间多次调用getSomeEntities - 但不是在运行时。这让我认为你的测试用例与你的应用代码有不同的处理。

相关问题