用JPA管理继承类的最佳方法是什么?

时间:2019-03-01 12:50:59

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

上下文

我有一个类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)

但是用这种表示形式,我可以有空值。例如,如果idauteurnull,则Oeuvre将是Magasine

那么,这种表示方法更好吗?

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)

1 个答案:

答案 0 :(得分: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是链接到Livre还是Magasine,则可以使用双向映射,例如:

@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()