【发布时间】:2016-11-09 12:28:45
【问题描述】:
今天我偶然发现了 EclipseLink 的一些意外行为。 (我不知道这是否绑定到 EclipseLink,或者这是否对所有 JPA 提供程序都相同。)
我假设当在同一个事务中(使用同一个 EntityManager)发出时,对托管 JPA bean 的检索总是返回对同一个对象实例的引用。
如果是这样,我不知道为什么我在执行以下测试用例时会收到错误:
@Test
public void test_1() {
EntityManager em = newEntityManager();
em.getTransaction().begin();
// Given:
Product prod = newProduct();
// When:
em.persist(prod);
em.flush();
Product actual =
em.createQuery("SELECT x from Product x where x.id = "
+ prod.getId(), Product.class).getSingleResult();
// Then:
assertThat(actual).isSameAs(prod); // <-- FAILS
em.getTransaction().commit();
}
标有“FAILS”的语句抛出以下 AssertionError:
java.lang.AssertionError:
Expecting:
<demo.Product@35dece42>
and actual:
<demo.Product@385dfb63>
to refer to the same object
有趣的是,以下稍作修改的测试成功了:
@Test
public void test_2() {
EntityManager em = newEntityManager();
em.getTransaction().begin();
// Given:
Product prod = newProduct();
// When:
em.persist(prod);
em.flush();
Product actual = em.find(Product.class, prod.getId());
// Then:
assertThat(actual).isSameAs(prod); // <-- SUCCEEDS
em.getTransaction().commit();
}
显然查找和查询对象是有区别的。
这是预期的行为吗?为什么?
--编辑--
我想我找到了问题的根源:Product 的 ID 类型为 ProductId。
以下是相关代码:
@Entity
@Table(name = "PRODUCT")
public class Product implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "ID", nullable = false)
@Converter(name = "productIdConverter", converterClass = ProductIdConverter.class)
@Convert("productIdConverter")
private ProductId id;
@Column(name = "NAME", nullable = false)
private String name;
[...]
}
@Convert and @Converter annotations 是 EclipseLink 特定的。
与 JPA 2.1 转换器不同,您可以将它们放在 ID 字段中。
但似乎在某些情况下,如果 bean 对其 ID 字段使用自定义类型,则 EclipseLink 在其会话缓存中查找托管 bean 时会遇到问题。
我想我必须为此提交一个错误。
【问题讨论】:
-
根据 JPA 规范,EntityManager 应该只为特定的“id”提供一个对象(引用)。该规则适用于查询以及查找......类似地,您可以使用 em.contains 来查看它是否“管理”特定对象。如果 EclipseLink 真的违反了这个规则,那么就提出一个错误。 DataNucleus JPA 在这方面工作得很好。
-
“产品”是否有一些奇怪的 equals/hashCode 方法?
-
好点。这是自定义 ID 类。谢谢你的提示。我已经分别更新了我的问题。
标签: jpa