为什么JPA查询返回不同的对象

时间:2013-02-06 12:11:59

标签: java-ee jpa

我在Java ee应用程序中使用JPA。我有一个映射到A类的表A.现在,当我查询实体时(例如,通过它的id),它总是返回一个不同的对象。例如:

public A getAById(int id) {
    A obj = (A) em.createNamedQuery("getAById")
            .setParameter("id", id).getSingleResult();
    return obj;
}

public void test(){
    getAById(1)==getAById(1) //is false
}

有没有办法告诉JPA在从数据库查询时不要总是创建新实例,而是在已经查询的情况下返回现有对象?

// EDIT 这篇文章给了我很多帮助: http://en.wikibooks.org/wiki/Java_Persistence/Caching#Example_JPA_2.0_Cacheable_annotation

3 个答案:

答案 0 :(得分:2)

有没有办法告诉JPA在从数据库查询时不要总是创建新实例,而是在已经查询的情况下返回现有对象?

JPA从不为选择查询创建新实体。请参阅以下示例

Query qry = em.createNamedQuery("SELECT r FROM BusinessUnit r WHERE r.buKey = :buKey");
Object result1 = qry.setParameter("buKey", "32").getSingleResult();
qry = em.createNamedQuery("SELECT r FROM BusinessUnit r WHERE r.buKey = :buKey");
Object result2 = qry.setParameter("buKey", "32").getSingleResult();
System.out.println(result1 == result2);

对于上述情况,我得到result1 == result2为真,因为hashCode方法在实体类中被覆盖。

如果您的实体类被equals方法覆盖,您可以使用equals方法进行测试,而不是与对象引用进行比较。

public void test(){
    getAById(1).equals(getAById(1)) ;
}

答案 1 :(得分:2)

JavaEE容器将JPA提供程序EntityManager包装在代理中,因此您看到的行为很可能是因为您的容器在事务之外获取了新的EM。你的两个getAById(1)调用将转到底层的两个不同的EntityManagers。您可以通过将它们包装在单个事务中来解决这个问题,从而迫使容器在事务的整个生命周期中使用相同的实体管理器。

答案 2 :(得分:-2)

如果持久性上下文中已存在实体实例,是的,但您需要使用 EntityManager 操作,例如 entityManager.find(entity.class) ,id);执行此操作将要求 entitymanager 首先从关联的 PersistenceContext 中查找实体,如果找不到它将从DB查询。一旦实体实例首次加载到关联的PersistenceContext中,针对相同实体ID的其余查询将不会触发数据库查询,并始终重用PersistenceContext中缓存的查询。这是JPA的第一级缓存。

使用 JPQL 不符合您的要求,因为 JPQL 会绕过 PersistenceContext 并始终直接与DB通信,最终会获得不同的java对象(主键相同的实体)。