【问题标题】:JPA Inheritance: Query fails with unknown columnJPA 继承:查询因未知列而失败
【发布时间】:2019-08-07 13:10:34
【问题描述】:

在使用 EclipseLink v2.7.4 时执行以下 JPA 查询:

SELECT pr FROM AbstractProduct pr WHERE pr.shelve.superMarket.id = :1 ORDER BY pr.sortOrder

给出以下错误:

Unknown column 't0.SORTORDER' in 'order clause'
Error Code: 1054
Call: SELECT t2.ID, t2.SORTORDER, t2.SHELVE_ID FROM APPLE t2, SHELVE t1 WHERE ((t1.SUPERMARKET_ID = ?) AND (t1.ID = t2.SHELVE_ID)) ORDER BY t0.SORTORDER
    bind => [12]

查询引用了 t0,但在生成的查询中没有任何地方定义 t0 是哪个表。

这些是我正在使用的实体:

@Entity
public class SuperMarket {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @OneToMany(mappedBy = "superMarket")
    List<Shelve> shelves;
}

@Entity
public class Shelve {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    protected SuperMarket superMarket;

    @OneToMany(mappedBy = "shelve")
    protected List<AbstractProduct> products;
}

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class AbstractProduct {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    protected Shelve shelve;

    protected long sortOrder;
}

@Entity
public class Apple extends AbstractProduct {

}

@Entity
public class Banana extends AbstractProduct {

}

这会导致以下查询:

CREATE TABLE SUPERMARKET (ID BIGINT NOT NULL, PRIMARY KEY (ID));
CREATE TABLE SHELVE (ID BIGINT NOT NULL, SUPERMARKET_ID BIGINT, PRIMARY KEY (ID));
CREATE TABLE APPLE (ID BIGINT NOT NULL, SORTORDER BIGINT, SHELVE_ID BIGINT, PRIMARY KEY (ID));
CREATE TABLE BANANA (ID BIGINT NOT NULL, SORTORDER BIGINT, SHELVE_ID BIGINT, PRIMARY KEY (ID));
CREATE TABLE ABSTRACTPRODUCT (SHELVE_ID BIGINT);

不应创建最后一个表 ABSTRACTPRODUCT,因为它是一个抽象 Java 实体,并且我使用的是按类继承样式的表。这似乎是 eclipselink 中的一个错误,它也在这个问题中进行了讨论:Understanding of TABLE_PER_CLASS with keys in eclipselink 它是继承与 OneToMany 关系的组合似乎触发了 create table 语句。不确定此错误是否与我在开始时提到的查询错误有关。我认为不是,因为该表甚至没有排序顺序字段。

当我删除 ORDER BY 子句时,查询将成功执行。当我将查询更改为仅向上一级时,它也会成功执行:

SELECT pr FROM AbstractProduct pr WHERE pr.shelve.id = :1 ORDER BY pr.sortOrder

对于测试,我摆脱了继承,让 Shelve 实体直接与 Apple 建立 OneToMany 关系,Apple 没有扩展任何其他类,在这种情况下,查询也成功执行。但我需要抽象类和继承。

知道为什么在这种情况下生成的查询是错误的吗?

正如下面的答案中所述,我可以使用不同的继承策略以不同的方式解决此问题。我选择每个类类型的表,因为这允许我在查询中使用抽象实体,而具体类获得一个包含所有字段的表。我希望这有助于在从具体类中进行大量插入和选择时提高性能,因为这只涉及单个数据库表。

更新 我认为这是 EclipseLink 中的一个错误,我创建了两个错误报告: https://bugs.eclipse.org/bugs/show_bug.cgi?id=549866 用于创建表的抽象类 https://bugs.eclipse.org/bugs/show_bug.cgi?id=549868查询错误

【问题讨论】:

  • 您提到的错误是在 2009 年报告并在 2014 年修复的。它不应该是您观察的原因(请参阅:bugs.eclipse.org/bugs/show_bug.cgi?id=265702)。但是,对于 EclipseLink 的最新版本存在一些争论,并且这些版本可能会再次受到奇怪/未定义行为的影响。
  • 我认为 265702 在某些情况下会倒退。不过,一般来说,每个类的表继承不是一个好的选择。必须对所有这些子类表执行相同的查询,并且排序是不切实际的。见en.wikibooks.org/wiki/Java_Persistence/…

标签: jpa inheritance eclipselink


【解决方案1】:

我能够使用继承策略TABLE_PER_CLASS 重现您的问题。一旦我将其更改为 InheritanceType.JOINED 并重新创建数据库的架构(在我的例子中:PostgreSQL 10.9),查询就会按预期执行。

所以代码应该改成:

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class AbstractProduct {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @ManyToOne
    protected Shelve shelve;

    protected long sortOrder;
}

注意

  1. 我将缺少的@ManyToOne 注释添加到属性shelve
  2. @ManyToOne 也应添加到实体Shelve 中的protected SuperMarket superMarket;

生成的数据库架构如下所示:

CREATE TABLE public.abstractproduct
(
    id bigint NOT NULL,
    dtype character varying(31) COLLATE pg_catalog."default",
    sortorder bigint,
    shelve_id bigint,
    CONSTRAINT abstractproduct_pkey PRIMARY KEY (id),
    -- FK definitions left out for brevity
)

例如,Apple 变成:

CREATE TABLE public.apple
(
    id bigint NOT NULL,
    CONSTRAINT apple_pkey PRIMARY KEY (id),
        -- FK definitions left out for brevity
)

希望对你有帮助。

【讨论】:

  • 感谢您对此进行调查。我知道使用其他类型的继承确实有效。但是,对于连接类型,抽象实体会获得自己的表。在主要对 Apple 或 Banana 表进行查询时,出于性能原因,我试图避免这种情况。这不应该也适用于每个类继承的表吗?我开始认为这是一个错误?
  • 如果操作正确,您将通过联合继承获得更好的性能,因为对抽象类的任何查询都意味着对每个子类表的单独查询。单表或联表;数据本身与您显示的内容相同,所以我不明白为什么无论如何您会有单独的类型信息,甚至是单独的产品类别 - 苹果的行为与香蕉的行为并没有太大的不同作为超市货架而言会不会?
  • 本题代码为示例类。在真正需要继承的地方,真实的对象要复杂得多。为了性能,我的主要用例是:能够查询抽象实体和(更经常)插入和读取具体实体。我打算试试单桌策略。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-21
  • 1970-01-01
  • 1970-01-01
  • 2017-07-22
  • 2016-01-29
相关资源
最近更新 更多