冬眠多态性

2022-09-04 19:45:30

这是一个Hibnerate多态性问题和一个数据模型设计问题;它们是交织在一起的。我过去使用过Hibernate,并且喜欢它,但有时我发现除了琐碎的设计之外,很难考虑任何事情。不是对冬眠的敲门声;只是一个观察,ORM总体上可能具有挑战性。

我认为这是一个Hibernate 101问题,但我不确定。我试图实现的目标甚至可能是不可能的。

我有一个抽象类水果,将被子类为苹果和橙子。我有一个Note类,表示有关Apples和Oranges的注释或注释。一个苹果或橙子可以有许多与之关联的Notes,但只有一个Apple或Orange会与给定的Note相关联。

以下是这些类的草图,我现在省略了对象id的位置,以及将它们与橙子区分开来的Apples的属性。目前,我对使用哪种Hibernate继承策略没有强烈的感觉。

abstract public class Fruit {
}

// Apples have notes written about them:
public class Apple extends Fruit {
   private Set<Note> note;
   ...

   @OneToMany(cascade = CascadeType.ALL)
   public Set<Note> getNote() {
       return note;
   }
}


// Oranges have notes written about them:
public class Orange extends Fruit {
   private Set<Note> note;
   ...

   @OneToMany(cascade = CascadeType.ALL)
   public Set<Note> getNote() {
       return note;
   }
}

下面是当前实现的 Note 类,其中我们看到它具有 Apple 和 Orange 的字段。这种设计的缺陷或不优雅之处在于,单个笔记实例只会指向Apple或Orange之一,而永远不会同时指向两者。因此,如果将Note绑定到Apple上,则橙色字段是多余的,不雅观的,反之亦然。

// A note about an Apple or Orange
public class Note {
   private String theNote;
   private Apple apple; 
   private Orange orange;
   ...

   // with the usual many to one mapping
   @ManyToOne
   @JoinColumn(name = "apple_id")
   public Apple getApple() { 
       return apple;
   }

   // with the usual many to one mapping
   @ManyToOne
   @JoinColumn(name = "orange_id")
   public Orange getOrange() {
       return orange;
   }

   ...
}

但是,这是我认为我想要基于我的设计基础的Note类,但我不确定如何考虑Hibernate注释和表映射:

// A note about a fruit:
public class Note {
   private String theNote;
   private Fruit fruit;
   ...
}

因此,水果将是苹果或橙子的实例。

后一个Note类,它引用了水果,实际上将容纳一个苹果或橙子,甚至可以与Hibernate ORM映射相协调吗?如果是,有人可以谈谈如何。


答案 1

这是绝对可能的。您可以将注释与抽象类相关联,而不是在每个实现中重复它们:Fruit

@Entity
@Inheritance
public abstract class Fruit {
   private Set<Note> notes;
   ...

   @OneToMany(cascade = CascadeType.ALL, mappedBy = "fruit")
   public Set<Note> getNotes() {
       return notes;
   }
}

@Entity
public class Apple extends Fruit {
   ...
}


@Entity
public class Orange extends Fruit {
   ...
}

@Entity
public class Note {
   private String theNote;

   @ManyToOne
   private Fruit fruit;
   ...
}

Ét voilà!

-- 根据注释添加:JPA 提供了多种处理继承的策略。Java EE 教程中的相关部分应该可以帮助您入门。

基本上,您的选项是:

  • 将所有内容存储在一个表中,并使用鉴别器列来了解哪一行是哪种类型
  • 将每个具体类(Apple 和 Orange)存储在单独的表中
  • 具有带有鉴别器列的通用水果表,以及具有水果表外键的 Apple 和 Orange 表

另一个编辑:请注意,这是一个休眠问题,而不是JPA问题。但是,不会有太大的区别,因为选项是相同的。以下是Hibernate文档中的相关部分


答案 2

这种模式在基于休眠的数据层中非常普遍。它在内部的工作方式很大程度上取决于所使用的继承策略。

当使用每个类的表或每个子类继承的表时,将为每个子类/类创建一个表。例如,在您的例子中,您将有一个表 Fruit 和两个表和 ,在 Fruit 和 Apple/Orange 之间具有外键引用。当通过ID请求单个水果(无论是苹果还是橙子)时,Hibernate将外部加入水果表和苹果和橙色表。每行都将转换为 Apple 或 Orange,具体取决于从哪个表中检索字段。AppleOrange

另一种可能性是使用鉴别器。将使用单个表,该表将包含一个鉴别器字段(例如 取值和 )。根据此字段的值,Hibernate 将确定相应的对象是 Apple 还是 Orange。Fruitfruit_typeappleorange

在你的例子中,在预先加载的情况下,当Hibernate加载Note对象时,它将急切地获取相应的水果,并相应地用Apple或Orange的实例填充水果田。

在延迟获取的情况下,fruit 字段将是实现 Fruit 接口的代理。在实际果田加载之前,其类型是不确定的。

希望这能回答您的一些疑问。


推荐