为什么JPA加入会返回太多结果?

时间:2012-03-14 22:16:49

标签: java jpa join jpql

如果我在SQL中查询:

select * from Profesor
inner join Estudiantes on Profesor.id = Estudiante.id
where Profesor.nombre = 'juan'
  and Estudiante.nombre = 'jose'

此查询返回profesor和学生。一位教授和一位学生。只有胡安教授和何塞为学生。

然后,如果我在JPA中查询:

select p from Profesor p
inner join p.estudiantes e
where p.nombre = juan
  and e.nombre = jose.

JPA将使用 all 返回profesor Juan,而不仅仅是我想要的学生,profesor.estudiantes将列出所有学生。

我的类型是:

class Profesor{
    private List<Estudiante> estudiantes;
}

class Estudiante{
    String matricula;
}

抱歉,我用西班牙语编码。我只想弄清楚这一点。

我不知道我的问题是否清楚,请告诉我。

1 个答案:

答案 0 :(得分:2)

你需要了解两件事。

首先:当你说Select p from Profesor时,JPA只选择Profesor表中的列,并返回一个Profesor实例,其中包含尚未加载的学生集合。第一次实际访问该集合时,它会被懒惰地加载。当它加载集合时,它忘记了用于加载教授的查询。它载入的是教授的学生集合。由于教授有很多学生,所以他们都是这样。初始查询类似于

select p.* from Profesor inner join ...

在SQL中执行它,你会发现它没有加载教授及其学生。它只会加载教授。

第二:实体应该代表数据库中的数据。它不应该代表查询的结果。因此,教授实体中的学生集合始终是教授所有学生的集合。

要做你想做的事,你有几个选择:

  1. select p, s from Profesor inner join p.students s...:这将返回一个包含找到的profesor和找到的学生的数组。
  2. 如果关联是双向的:select s from Profesor p inner join p.students s ...:这将加载学生,并引用其教授
  3. 如果使用Hibernate,这违反了该领域的JPA规范:select p from Profesor inner join fetch p.students...:fetch使hibernate在单个查询中加载教授及其学生。但是既然你在学生身上添加了一个where子句,它只会加载教授的匹配学生。
  4. 请注意,第三种解决方案非常危险,我不建议使用它。首先是因为它不是有效的JPQL。更重要的是,因为获得由这样的查询加载的教授实体的代码期望教授.getStudents()将返回教授的所有学生,而不仅仅是其中之一。因此,它可能会显示错误的结果,或修改集合并导致数据库中的不连贯。