【问题标题】:What is the best way to manage inherit class with JPA?使用 JPA 管理继承类的最佳方法是什么?
【发布时间】:2019-07-23 12:17:35
【问题描述】:

上下文

我有一个类Oeuvre,还有两个子类LivreMagasineAuteur 链接到 Livre

所以类图看起来像:

       Oeuvre
          |
     ____________
    |            |
Magasine        Livre____Auteur

实体

@Entity
public class Oeuvre {

    @Id
    @GeneratedValue(generator="system-uuid")
    @GenericGenerator(name="system-uuid", strategy = "uuid")
    private String idoeuvre;

    @NotNull
    private String titre;

    @NotNull
    private String ISBN;

    @ColumnDefault("CURRENT_DATE()")
    private LocalDate parution;

}

@Entity
public class Magasine extends Oeuvre {

    @Enumerated(EnumType.STRING)
    private Type type;
}

@Entity
public class Livre extends Oeuvre {

    @ManyToOne
    @JoinColumn(name = "idauteur")
    private Auteur auteur;    
}

@Entity
public class Auteur {

    @Id
    @GeneratedValue(generator="system-uuid")
    @GenericGenerator(name="system-uuid", strategy = "uuid")
    private String idauteur;

    @NotNull
    private String nom;

    @NotNull
    private String prenom;
}

H2 表示

Oeuvre(idoeuvre, isbn, parution, titre, type, idauteur)

但是通过这种表示,我可以有空值。例如,如果 OeuvreMagasineidauteur 将是 null

那么,这种表示方式不是更好吗?

Oeuvre(idoeuvre, isbn, parution, titre)
Livre(idlivre, idauteur, idoeuvre)
Magasine(idmagasine, type, idoeuvre)

SQL 脚本

使用当前的数据库方案,我无法创建此约束:ALTER TABLE livre ADD FOREIGN KEY (idauteur) REFERENCES auteur(idauteur) ON UPDATE CASCADE

结论

可以用JPA/H2来表示吗?

感谢您的回答。

编辑 1:问题

JPA/H2可以有这个数据库方案吗?

Oeuvre(idoeuvre, isbn, parution, titre)
Livre(idlivre, idauteur, idoeuvre)
Magasine(idmagasine, type, idoeuvre)

【问题讨论】:

  • 你的问题描述让我有点困惑,你打算有一个名为Oeuvre 的表,还是有两个表,一个用于Livre,另一个用于@ 987654338@?
  • 说实话,我不知道最好的方法是什么,你能告诉我吗?
  • 如果你不想要那些null 问题,并且你想为Livre 定义一个约束,那么我认为你回答了你自己的问题,并且你想要一个单独的表来为@987654341 @ 和 Magasine.
  • 我不能创建三个实体? OeuvreLivreMagasine?因为如果我只创建LivreMagasine,我就不能像ALTER TABLE exemplaire ADD FOREIGN KEY (idoeuvre) REFERENCES oeuvre(idoeuvre) ON UPDATE CASCADE 那样创建约束,对吧? Exemplaire 是链接到 Oeuvre 的实体
  • @g00glen00b 我编辑了我的帖子。

标签: spring-boot jpa spring-data-jpa h2


【解决方案1】:

按照您描述实体的方式,Hibernate 将期望以下数据库结构:

oeuvre(idoeuvre, titre, isbn, parution)
magasine(idoeuvre, titre, isbn, parution, type)
livre(idoeuvre, titre, isbn, parution, idauteur)

这将导致大量重复,并且可能不是您想要的。现在,您确实描述了两种可能的解决方案,即:

  1. 定义一个名为oeuvre的表,其中每个字段都存在,例如:

    oeuvre(idoeuvre, titre, isbn, parution, type, idauteur)
    
  2. livremagasine 定义单独的表,例如:

    oeuvre(idoeuvre, titre, isbn, parution)
    magasine(idmagasine, idoeuvre, idauteur)
    livre(idlivre, idoeuvre, idauteur)
    

现在,正如您提到的,第一个解决方案不允许您定义约束,而第二个解决方案可以。基本上,这意味着您提供了自己的答案,如果您需要一个约束,那么您必须区分不同的类型,然后您必须采用第二种方法。

然后您可以定义以下约束:

  • 每个magasine.idoeuvrelivre.idoeuvre 都应该是唯一的(您甚至可以将它们用作主键)
  • livre.idauteur 应该是 not null

但是,在这种情况下,您不需要任何继承。 Hibernate 中的继承通常仅在多个表具有相同字段的情况下进行,而在这种情况下,您有三个单独的表,因此三个单独的实体,不涉及继承:

@Entity
public class Oeuvre {
    @Id
    @GeneratedValue(generator="system-uuid")
    @GenericGenerator(name="system-uuid", strategy = "uuid")
    private String idoeuvre;

    @NotNull
    private String titre;

    @NotNull
    private String ISBN;

    @ColumnDefault("CURRENT_DATE()")
    private LocalDate parution;
}

@Entity
public class Magasine { // No extends
    @Id
    @GeneratedValue(generator="system-uuid")
    @GenericGenerator(name="system-uuid", strategy = "uuid")
    private String idmagasine;

    @OneToOne
    @JoinColumn(name = "idoeuvre")
    private Oeuvre oeuvre;

    @Enumerated(EnumType.STRING)
    private Type type;
}

@Entity
public class Livre { // No extends
    @Id
    @GeneratedValue(generator="system-uuid")
    @GenericGenerator(name="system-uuid", strategy = "uuid")
    private String idlivre;

    @OneToOne
    @JoinColumn(name = "idoeuvre")
    private Oeuvre oeuvre;

    @ManyToOne
    @JoinColumn(name = "idauteur")
    private Auteur auteur;   
}

在这种情况下您可以使用继承的唯一原因是因为MagasineLivre 都具有相似的Oeuvre 映射,在这种情况下,您可以像这样更改您的代码:

@Entity
public class Oeuvre {
    @Id
    @GeneratedValue(generator="system-uuid")
    @GenericGenerator(name="system-uuid", strategy = "uuid")
    private String idoeuvre;

    @NotNull
    private String titre;

    @NotNull
    private String ISBN;

    @ColumnDefault("CURRENT_DATE()")
    private LocalDate parution;
}

@MappedSuperclass // No @Entity
public class SpecificOeuvre {
    @OneToOne
    @JoinColumn(name = "idoeuvre")
    private Oeuvre oeuvre;
}

@Entity
public class Magasine extends SpecificOeuvre {
    @Id
    @GeneratedValue(generator="system-uuid")
    @GenericGenerator(name="system-uuid", strategy = "uuid")
    private String idmagasine;

    @Enumerated(EnumType.STRING)
    private Type type;
}

@Entity
public class Livre extends SpecificOeuvre {
    @Id
    @GeneratedValue(generator="system-uuid")
    @GenericGenerator(name="system-uuid", strategy = "uuid")
    private String idlivre;

    @ManyToOne
    @JoinColumn(name = "idauteur")
    private Auteur auteur;   
}

两者都代表相同的数据库结构,唯一的区别是其中一个有一个单独的类,用@MappedSuperclass注释,其中包含MagasineLivre之间的所有可重用信息,在这种情况下只是映射到Oeuvre

如果您想知道Oeuvre 是否链接到LivreMagasine,您可以使用双向映射,例如:

@Entity
public class Oeuvre {
    @Id
    @GeneratedValue(generator="system-uuid")
    @GenericGenerator(name="system-uuid", strategy = "uuid")
    private String idoeuvre;

    @NotNull
    private String titre;

    @NotNull
    private String ISBN;

    @ColumnDefault("CURRENT_DATE()")
    private LocalDate parution;

    @OneToOne(mappedBy = "oeuvre")
    private Livre livre;

    @OneToOne(mappedBy = "oeuvre")
    private Magasine magasine;
}

现在您可以查看是否给出了oeuvre.getMagasine()oeuvre.getLivre()

【讨论】:

  • 感谢这个非常明确的答案。但是,我有一个问题,对于第二种解决方案,我无法从Oeuvre 知道它是Livre 还是Magasine 的实例,因为它们不是从Oeuvre 扩展的。有没有使用@MappedSuperclass 的解决方案?还是需要sql请求?
  • @N.Lamblin 您可以通过在Oeuvre 中使用@OneToOne(mappedBy = "oeuvre") 注释来使用双向映射。我用一个例子编辑了我的答案。在屏幕后面,这将为您执行额外的 SQL 请求,或者在可能的情况下应用连接。
  • 谢谢 :) 我需要的最后一件事是必须在 oeuvre / livreoeuvre / exemplaire 中使用相同的 ID。确实,拥有 2 个不同的 id 有点愚蠢......为此,我可以使用像 Livre(String idoeuvre, Auteur auteur) { this.idlivre = idoeuvre; ...} 这样的构造函数吗?或将idoeuvre 定义为@id ?
  • @N.Lamblin 这并不容易,每个实体都必须有一个@Id 字段,如果您不想丢失@OneToOneOeuvre 的映射,您可以必须映射idoeuvre 两次。但是,Hibernate 不允许您使用,除非您将其中之一定义为 updatable = falseinsertable = false。你可以在这里阅读更多相关信息:stackoverflow.com/a/3807285
  • 好的,所以我会保留idlivreidoeuvre 并在idoeuvre 上添加updatable = false, insertable = false,即使我觉得这有点脏啊哈。谢谢!
猜你喜欢
  • 1970-01-01
  • 2021-02-05
  • 2021-06-15
  • 1970-01-01
  • 1970-01-01
  • 2015-09-20
  • 2017-12-26
  • 1970-01-01
  • 2020-02-10
相关资源
最近更新 更多