Каков наилучший способ управления наследованием классов с помощью JPA? - PullRequest
0 голосов
/ 01 марта 2019

Контекст

У меня есть класс Oeuvre и два подкласса Livre и Magasine.Auteur связан с 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)

Но с этим представлением у меня могут быть пустые значения.Например, idauteur будет null, если 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 голосов
/ 01 марта 2019

Как вы описали свои сущности, 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. Определение отдельных таблиц для livre и magasine, например:

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

Теперь, как вы упоминали, первое решение не позволяетВы должны определить ограничение, в то время как второй делает.По сути, это означает, что вы предоставили свой собственный ответ, если вам нужно ограничение, тогда вам придется различать разные типы, а затем вам придется пойти на второй подход.

Тогда вы можетеОпределите следующие ограничения:

  • Каждый magasine.idoeuvre и livre.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;   
}

Единственная причина, по которой вы моглив этом случае используйте наследование, потому что оба Magasine и Livre имеют одинаковое отображение 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, который содержит всю повторно используемую информацию между Magasine и Livre, что в данном случае является только отображением 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().

...