【问题标题】:Class of the entities retrieved from a hierarchy从层次结构中检索的实体的类
【发布时间】:2026-02-02 12:45:01
【问题描述】:

在我的项目中,我有一个 JPA 层次结构 Location -> Site

@Entity
@Table(name = "LOCATION")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="LOC_TYPE",discriminatorType=DiscriminatorType.STRING)
public class Location {
  ...
}

@Entity
@DiscriminatorValue("SI")
public class Site extends Location {
  ...
}

现在,每个Employee 都有一个分配了Location 的列表(即使据我所知,所有这些位置实际上都是Site)。在某些部分,我需要Sites 的列表,Employee 已分配给(注意,我定义的关系是Locations)。

使用 Hibernate 3.2,我可以只编写 Location 的查询并按判别式过滤;返回的类是更具体的子类的实例,v.g.:

Query query = em.createQuery("SELECT loc FROM Location loc WHERE loc.type=\"SI\"");
List<Location> locations = (List<Location>) query.getResultList();
for (Location location : locations) {
  Site mySite = (Site) location;
  ...
}

但是,我找不到任何文档来说明这种行为是由 JPA 规范保证还是仅仅是 Hibernate 的实现决定。在最后一种情况下,我不应该使用这种方法,因为如果我切换供应商,它可能会改变)。

你能告诉我标准是否支持我的方法吗?

顺便说一句,我在 Hibernate 3.2 上使用 JPA 1

更新:

为了澄清我想要什么,这是我实施的“安全警报”。请记住,disc 属性是鉴别器列,因此我确信返回的对象会作为Site 实例持续存在。

Query query = em.createQuery("SELECT lo FROM Employee em JOIN em.location lo WHERE lo.disc = 'SI'");
List<Location> locations = (List<Location>) query.getResultList();
List<Site> site = new ArrayList<Site>();
for (Location location : locations) {
  if (location instanceof Site) {
    sites.add((Site) location);
  } else {
    log.warn("Found a Location that is not instance of Site");
  }
}

JPA 规范是否指定我的log.warn 语句永远不会被调用? AFAIK,也许一个实现可以返回一堆Location,它们不是Site 的实例。当然,在这种情况下,只有Location 属性可用;这种行为有点像 C++ 中的切片。

【问题讨论】:

  • 我认为你仍然没有解释你的问题。您是否有一个映射到LOC_TYPE DB 列的Java 字段disc?如果没有,那么您可以简单地尝试一下。此外,在您的示例中:为什么不直接查找站点实体:SELECT s FROM Site s WHERE...
  • 我的 JPQL 有点过于简单化了,现在它更具代表性。我不能只选择Site 实体,因为我通过导航到Location 超类来获取它们。作为最后一个解决方案,我可以使用一个子选择来解决它,该子选择将 ID 从Locations 返回到Sites 的选择,但这个问题让我想知道这个问题(另外,我尽量避免@ 987654345@ 子选择以解决性能问题)。
  • 1.子选择比IN 差。 2. IN 从来都不是问题,只要你没有 1000 个值。 3. 你没有说我的第一个想法(也许你已经使用了):只需添加一个映射到LOC_TYPE DB 鉴别器列的 JAVA 字段 eType 并在查询中使用它。
  • 如果您只要求使用鉴别器列的站点类型的记录,为什么您认为您会获得其他信息。 ?这是一个问题,如果该事件是可能的?
  • @Koitoer 问题是:返回的实例将是Site 类型?我的 JPQL 需要 Location,因此它可以像 C++ 对对象进行切片并返回带有行内容的 Location 实例。显然,Site 中定义的属性将不可用(就像切片一样),但我仍然会得到 JPQL 查询的要求。我的问题可以改写为:我可以确定我将获得最初用于存储实体的同一类(或该类的代理)的实例吗?扭曲,我知道,但没有找到阻止它的规范。

标签: jpa


【解决方案1】:

在从 2.0 版开始的 JPA 中,您有 TYPE(e) 函数。规范中的示例:

SELECT e FROM Employee e WHERE TYPE(e) IN (Exempt, Contractor)

摘自 JPA 2.0 规范(4.6.17.4 实体类型表达式):

实体类型表达式可用于限制查询多态性。 TYPE 运算符返回参数的确切类型。


注意 JPA。 另一方面,您的示例对基本 JPA 机制提出了质疑,我想再次强调这一点:当您从带有鉴别器的 DB 中获取一行(使用 JPA 查询)时特定于 Site 实体的值,您可以确定 Java 对象将是该类的实例(即 Site 类)。现在我相信规范不会强制提供者其类为Site(即fetchedEntity.getClass()==Site.class),但您可以100% 确定fetchedEntity instanceof Site 返回true。


注意 Hibernate。根据我使用 Hibernate 的经验,我只能补充一点,fetchedEntity.getClass() == Site.class 在 Hibernate 中返回 true(当然除了 fetchedEntity instanceof Site 的事实之外)。

【讨论】:

  • 感谢您的提示,很高兴知道此选项。无论如何,WHERE 部分已经被覆盖,因为我通过鉴别器过滤,我想要的是确保检索到的e 实际上已创建为ExemptContractor 实例(而不仅仅是一个普通的Employee)
  • “部分已被覆盖”是什么意思?据我了解,这正是您所需要的(如果 JPA 1.0 支持):这些员工将完全属于这些 Java 类型:ExemptContractor,而不是普通的 Employee
  • JPA 1 不支持TYPE(我刚刚检查过)。我通过鉴别器过滤来解决它(如我的JPQL)。无论如何,我想要的是确保您最后确认的规范部分(或没有它);我可能有点太担心了,但我觉得,如果文档没有指定,某些实现可能会返回一个普通的 Employee 实体而不是原始子类(因为这就是 JPQL“请求”)
  • 嗯,这个例子来自 SPEC(正如我之前写的)。我从规范中添加了有趣的部分。不仅如此,我只能建议单独打开规范:)
  • 对问题进行了澄清。