【问题标题】:How can I work-around this Hibernate Limitation?我该如何解决这个 Hibernate 限制?
【发布时间】:2023-03-31 04:42:01
【问题描述】:

我在这样的层次结构中有 3 个实体:

         MyInterface
              |
      -----------------
      |               |
   Entity1          Entity2

MyInterface 没有在 Hibernate 中映射(因为我使用 implicit polymorphism strategy 来映射这个继承)

事实上,如果我启动这样的查询:

"FROM MyInterface"

它工作得很好(因为它检索了 Entity1 的所有实例和 Entity2 的所有实例,将它们放在一起,然后返回一个List<MyInterface>)。

如果我们看一下 Hibernate 生成的 SQL,它会启动 2 个独立的 SQL 查询来首先检索 Entity1 实例,然后另一个查询来检索 Entity2 实例,但我对此很好。

当您尝试执行以下操作时,就会出现大问题:

"FROM MyInterface ORDER BY someField"

因为它将 ORDER BY 应用于第一个 SQL 查询,然后将相同的 ORDER BY 应用于第二个 SQL 查询,而不是将它们应用于 WHOLE 查询(我知道这一点,因为我可以看到Hibernate 启动的本机 SQL 查询)。

这显然是 Hibernate 的错误行为。

如何解决这个问题以强制 Hibernate 将 ORDER BY 应用于整个查询? (我不能在内存中这样做,因为稍后我还必须添加分页)。

【问题讨论】:

  • 你能提供你正在使用的映射吗?
  • 我将实体映射为独立实体(没什么特别的),我只是在 Java 级别(而不是在 Hibernate 中)使它们成为 MyInterface 的实现者。这是一个艰难的简化,所以我没有得到实体的映射 XML,但映射与任何其他常规实体相同,只是实现了一个通用的 MyInterface,没有别的。
  • 好吧,尝试编写一个 SQL 查询,从两个不同的表中进行选择并按一个公共字段排序。恕我直言,这是不可能的,因此 hibernate 也不能这样做。
  • UNION 给你一些空间。无论如何,Hibernate 为 HQL 的含义定义了一个语义,由于底层技术的限制,它没有实现这些语义。恕我直言,Hibernate 应该尽可能使用 SQL 来克服这个限制,即在内存中进行计算以维护语言的语义。
  • 我认为在 someField 构建集合时在休眠状态下进行合并会很便宜。

标签: java sql hibernate orm


【解决方案1】:

我想说问题在于 Hibernate 必须创建这 2 个 SQL 查询,因为您必须从 2 个表中读取数据。

我不确定在普通 SQL 中是否可以在一个查询中从 2 个表中读取并按 2 列(每个表中的一个列)进行排序(意味着没有特定于供应商的扩展),如果不是,Hibernate 将不得不这样做无论如何都要在内存中排序。

应用分页时您可以做的是:仅读取要排序的 id 和值(不是整个实体),然后在内存中排序并读取页面中包含的所有 id 的整个实体。为了使分页保持一致,您可能必须存储初始查询的结果(id + 订单条件)。

【讨论】:

    【解决方案2】:

    您的思维方式无法照原样映射到 SQL。假设您有 Entity1 字段 field1Afield1B ... 和 Entity2 字段 field2Afield2B、... 现在您希望执行以下查询:

    SELECT Entity1.* FROM Entity1
    UNION
    SELECT Entity2.* FROM Entity2
    ORDER BY CommonField
    

    这在 SQL 世界中是不可能的,因为实体具有不同数量的字段和不同的字段类型。

    因此,您需要考虑将常用字段提取到单独的表 CommonEntity 中,将您的界面转换为独立实体,并与 Entity1Entity2 一对一映射(请参阅 Table per subclass)。然后 SQL 将如下所示:

    SELECT * from CommonEntity LEFT OUTER JOIN Entity1 ON Entity1.refId = CommonEntity.id LEFT OUTER JOIN Entity2 ON Entity2.refId = CommonEntity.id
    ORDER BY CommonField
    

    或者你可以在你的表上创建一个视图并引入一个人为的鉴别器(鉴别器可以“区分”不同表中的 ID,这会导致 your solution 出现问题),然后将一个实体映射到这个视图(所以我们得到Table per class hierarchy):

    CREATE VIEW EntityAandEntityB AS
    SELECT 'A' as discriminator, Entity1.ID, CommonField1, ... CommonFieldZ, Entity1.field1A, ... Entity1.field1N, NULL, NULL,                          ... NULL(M)
    FROM Entity1
    UNION
    SELECT 'B' as discriminator, Entity2.ID, CommonField1, ... CommonFieldZ, NULL, NULL,                          ... NULL(N), Entity2.field2A, ... Entity2.field2M
    FROM Entity2
    ORDER BY CommonField1, ...
    

    其他替代方案(例如@UdoFholl 提到的,这也是EntityAandEntityB 的一种“外连接”)将产生2 个SQL,因此无法订购“整个”查询和scrolling is not possible

    【讨论】:

    • 这听起来不错并且是一个创造性的解决方案,但我会失去我需要的 polimorfism,因为 Entity1 和 Entity2 都有自定义数据和行为,我需要列表包含 Entity1 和 Entity2 的 polimorfic 实例。 ..
    • @edutesoy:我完全理解您的目标,我也想知道如何实现这一目标,并且非常惊讶地看到“解决方案”(甚至是错误的)。但是换个角度看问题:(1)SQL不保证行的顺序(2)Hibernate看到X这行的时候,如何决定创建哪个实体?为了解决这个问题,它需要一个鉴别器或超级实体。
    【解决方案3】:

    如果您使用以下内容,Hibernate 会为您执行此操作。

    @Entity
    @Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
    public abstract class AbstractEntity implements MyInterface {
        private int someField;
    }
    

    然后让您的子类执行此操作

    @Entity
    @Table(name="entity_1")
    public class EntityOne extends AbstractEntity {
        private int someOtherField;
    }
    
    @Entity
    @Table(name="entity_2")
    public class EntityTwo extends AbstractEntity {
        private int anotherSomeOtherField;
    }
    

    然后,您应该能够编写这样的查询来获得一个由数据库执行排序的联合 SQL 查询。

    FROM AbstractEntity ORDER BY someField
    

    【讨论】:

    • 我需要额外的物理表来执行此操作吗?因为我无法更改基础表,所以我无法添加这个新表。
    • 不,这会将两个具体实体映射到它们自己的表中。两个表都需要 someField 列,并且该列在两个表中的类型必须相同。其他列可以是实体特定的。
    • 我的问题是,对于这种映射继承,我需要这样做docs.jboss.org/hibernate/core/3.3/reference/en/html/…,它迫使我为实体的 KEY 使用相同的表字段名称。而且我的 Table 模型被锁定,并且表的 KEY 类型相同,但名称不同......我不能使用这种映射。这是一个很好的建议。
    • 你确定不能用吗?我认为您可以将每个子类中的 id 映射到不同的字段。 ID 生成是限制。有关详细信息,请参阅docs.jboss.org/hibernate/annotations/3.5/reference/en/…。值得一试。
    • 是的,我已经尝试过了,但您无法在子类元素中指定 ID……很遗憾。
    猜你喜欢
    • 2011-09-04
    • 2021-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-30
    • 1970-01-01
    相关资源
    最近更新 更多