Héritage

Méthode 1: une table par table concrète
@MappedSuperclass
public abstract class Watchable {
// Pas de Id pour cette classe mere
@Column(nullable = false)
protected String name;
protected String description;
// ... Getters et setters
}
Langage du code : PHP (php)
@Entity
public class Movie extends Watchable {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private Certification certification;
// ...
}
Langage du code : PHP (php)
@Entity
public class TVShow extends Watchable {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private int seasons;
// ...
}
Langage du code : PHP (php)


En base de données deux tables movie et tv_show et la classe Watchable n’aura pas de représentation
Les attributs name et description de Watchable seront ajoutés comme colonnes dans les tables enfants movie et tv_show
L’attribut Id de Watchable est reporté dans les classes enfants mais on peut le mettre explicitement dans Watchable. Ce sera l’objet de la 2 ième solution.
L’avantage de cette solution, c’est qu’elle est assez simple à mettre en œuvre.
Les Inconvénients de cette solution:
Nous aurons un select par classe concrète.
Un autre inconvénient aussi, c’est que si ici un attribut change, il va falloir répercuter ces changements sur toutes les tables des classes concrètes.
Et enfin, un dernier inconvénient pour cette solution, c’est tout ce qui est association.
En effet, vous ne pourrez pas mettre d’association dans la classe Watchable (Exemlpe, Hibernate ne sera pas capable d’associer les reviews avec Watchable)
Méthode 2: TABLE_PER_CLASS – une solution dérivée de la première
l’ID a été remontée au niveau de Watchable et il ne sera plus présent dans Movie et TVShow
Watchable, a maintenant un @Entity et @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
On va définir donc une stratégie table par classe.

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Watchable {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
protected Long id;
@Column(nullable = false)
protected String name;
protected String description;
//
}Langage du code : PHP (php)
@Entity
public class Movie extends Watchable {
private Certification certification;
// ...
}Langage du code : PHP (php)
@Entity
public class TVShow extends Watchable {
private int seasons;
// ...
}
Langage du code : PHP (php)
Méthode 3: SINGLE_TABLE

Pour ce type d’héritage, seule la table mère est représentée en base et la colonne bd-type est une colonne disciminator pour distinguer le type d’enregistrement soit movie ou soit tvshow
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "bd_type")
public abstract class Watchable {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
protected Long id;
@Column(nullable = false)
protected String name;
protected String description;
// ...
}Langage du code : PHP (php)
@Entity
@DiscriminatorValue("movie")
public class Movie extends Watchable {
private Certification certification;
// ...
}Langage du code : PHP (php)
@Entity
@DiscriminatorValue("tvshow")
public class TVShow extends Watchable {
private int seasons;
// ...
}Langage du code : PHP (php)
Faison maintenant un test sql reliant la table mère watchable evec review
@Test
public void inheritance_getAll() {
List<Watchable> all = entityManager
.createQuery("select distinct w from Watchable w left join fetch w.reviews", Watchable.class)
.getResultList();
assertThat(all).hasSize(3);
assertThat(all.stream().filter(o -> o.getClass() == TVShow.class)).hasSize(1);
assertThat(all.stream().filter(o -> o.getClass() == Movie.class)).hasSize(2);
}
Langage du code : JavaScript (javascript)
Et voici la requête générée par Hibernate:
select distinct watchable0_.id as id2_4_0_,
reviews1_.id as id1_3_1_,
watchable0_.description as descript3_4_0_,
watchable0_.name as name4_4_0_,
watchable0_.certification as certific5_4_0_,
watchable0_.seasons as seasons6_4_0_,
watchable0_.bd_type as bd_type1_4_0_,
reviews1_.author as author2_3_1_,
reviews1_.content as content3_3_1_,
reviews1_.movie_id as movie_id4_3_1_,
reviews1_.movie_id as movie_id4_3_0__,
reviews1_.id as id1_3_0__
from Watchable watchable0_
left outer join Review reviews1_ on watchable0_.id = reviews1_.movie_idLangage du code : JavaScript (javascript)
Hibernate nous fait juste un select distinct donc avec watchable et le left join sur review parce qu’on lui a demandé.
Il se sert de la colonne disciminant bd_type pour distinguer les lignes enfants soit movie ou soit tvshow.
Cette solution a l’avantage d’être simple à comprendre en termes de code, de bases de données et donc en termes de requêtes SQL.
C’est aussi la solution la plus performante.
Le seul désavantage à cette solution, ce sont les contraintes d’intégrité au niveau de la base de données.
En effet, si on a une colonne qui est propre à l’une des sous classes et qu’on ne veut pas qu’elle soit nulle par exemple, ça ne sera pas possible avec cette solution.
Méthode 4: JOINED – Héritage en BDD et dans le code

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Watchable {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
protected Long id;
@Column(nullable = false)
protected String name;
protected String description;
// ...
}
Langage du code : PHP (php)
@Entity
public class Movie extends Watchable {
private Certification certification;
// ...
}Langage du code : PHP (php)
@Entity
public class TVShow extends Watchable {
private int seasons;
// ...
}Langage du code : PHP (php)
En base cela donne:
CREATE TABLE watchable
(
id BIGINT NOT NULL,
name VARCHAR(255) NOT NULL,
description VARCHAR(255),
CONSTRAINT pk_watchable PRIMARY KEY (id)
);
CREATE TABLE movie
(
id BIGINT NOT NULL,
certification INTEGER,
CONSTRAINT pk_movie PRIMARY KEY (id)
);
CREATE TABLE tvshow
(
id BIGINT NOT NULL,
seasons INTEGER NOT NULL,
CONSTRAINT pk_tvshow PRIMARY KEY (id)
);
// Foreign key watchable_id dans movie et tvshow
ALTER TABLE movie
ADD CONSTRAINT FK_MOVIE_ON_ID FOREIGN KEY (id) REFERENCES watchable (id);
ALTER TABLE tvshow
ADD CONSTRAINT FK_TVSHOW_ON_ID FOREIGN KEY (id) REFERENCES watchable (id);Langage du code : PHP (php)

Faison ensuite un test sql en associant la table mère watchable avec celle de review qui elle est en association avec movie et observons la requete générée par Hibernate

@Test
public void inheritance_getAll() {
List<Watchable> all = entityManager
.createQuery("select distinct w from Watchable w left join fetch w.reviews", Watchable.class)
.getResultList();
assertThat(all).hasSize(3);
assertThat(all.stream().filter(o -> o.getClass() == TVShow.class)).hasSize(1);
assertThat(all.stream().filter(o -> o.getClass() == Movie.class)).hasSize(2);
}Langage du code : JavaScript (javascript)
select distinct watchable0_.id as id1_9_0_,
reviews1_.id as id1_7_1_,
watchable0_.description as descript2_9_0_,
watchable0_.name as name3_9_0_,
watchable0_1_.certification as certific1_3_0_,
watchable0_2_.seasons as seasons1_8_0_,
case
when watchable0_1_.id is not null then 1 -- Movie
when watchable0_2_.id is not null then 2 -- TVShow
when watchable0_.id is not null then 0 -- Review
end as clazz_0_,
reviews1_.author as author2_7_1_,
reviews1_.content as content3_7_1_,
reviews1_.movie_id as movie_id5_7_1_,
reviews1_.rating as rating4_7_1_,
reviews1_.movie_id as movie_id5_7_0__,
reviews1_.id as id1_7_0__
from Watchable watchable0_
left outer join Movie watchable0_1_ on watchable0_.id = watchable0_1_.id
left outer join TVShow watchable0_2_ on watchable0_.id = watchable0_2_.id
left outer join Review reviews1_ on watchable0_.id = reviews1_.movie_idLangage du code : JavaScript (javascript)
Hibernate nous génère systématiquement une jointure avec ses tables enfants movie et tvshow plus la table review parce qu’on l’a demandé.
On remarque le la clause sql case / when utilisé par Hibernate afin d’identifier chaque ligne de résultat distinct par un foreign key d’héritage (movie et tvshow) ou autre(review). Autrement dit, il calcule entre guillemets le discriminant en faisant un case sur la valeur des id.
Par rapport à notre base de données, on s’attend donc à avoir 3 lignes de résultats: 2 movies et un tvshow

Cette dernière solution n’est pas très recommandable car peu performante au niveau des requêtes complexes générées par Hibernate que ce soit pour les selects ou inserts.