Reputation: 1595
Context
I have a class Oeuvre
, and two sub class Livre
and Magasine
. An Auteur
is linked to a Livre
.
So the class diagram looks like :
Oeuvre
|
____________
| |
Magasine Livre____Auteur
Entities
@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 representation
Oeuvre(idoeuvre, isbn, parution, titre, type, idauteur)
But with this representation I can have empty values. For example idauteur
will be null
if Oeuvre
is a Magasine
.
So, this representation is not better?
Oeuvre(idoeuvre, isbn, parution, titre)
Livre(idlivre, idauteur, idoeuvre)
Magasine(idmagasine, type, idoeuvre)
SQL script
With the current database scheme I can't create this constraint : ALTER TABLE livre ADD FOREIGN KEY (idauteur) REFERENCES auteur(idauteur) ON UPDATE CASCADE
Conclusion
Is it possible to represent it with JPA/H2?
Thanks for your answers.
EDIT 1 : Question
Is it possible to have this database scheme with JPA / H2?
Oeuvre(idoeuvre, isbn, parution, titre)
Livre(idlivre, idauteur, idoeuvre)
Magasine(idmagasine, type, idoeuvre)
Upvotes: 0
Views: 92
Reputation: 44745
The way you described your entities, Hibernate will expect the following database structure:
oeuvre(idoeuvre, titre, isbn, parution)
magasine(idoeuvre, titre, isbn, parution, type)
livre(idoeuvre, titre, isbn, parution, idauteur)
This will lead to a lot of duplication, and is probably not what you want. Now, you did describe two possible solutions though, namely:
Defining one table called oeuvre
, where each field is present, for example:
oeuvre(idoeuvre, titre, isbn, parution, type, idauteur)
Definining separate tables for livre
and magasine
, for example:
oeuvre(idoeuvre, titre, isbn, parution)
magasine(idmagasine, idoeuvre, idauteur)
livre(idlivre, idoeuvre, idauteur)
Now, as you did mention, the first solution doesn't allow you to define a constraint, while the second one does. Basically, that means you provided your own answer, if you need a constraint, then you'll have to differentiate between the different types, and then you'll have to go for the second approach.
You could then define the following constraints:
magasine.idoeuvre
and livre.idoeuvre
should be unique (you could even use them as a primary key)livre.idauteur
should be not null
However, in this case, you don't need any inheritance. Inheritance within Hibernate is usually only done if multiple tables have the same fields, while in this case you have three separate tables, and thus three separate entities, no inheritance involved:
@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;
}
The only reason why you could use inheritance in this case is because both Magasine
and Livre
both have a similar Oeuvre
mapping, in that case, you could change your code like this:
@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;
}
Both represent the same database structure, the only difference is that one of them has a separate class annotated with @MappedSuperclass
that contains all re-usable information between Magasine
and Livre
, which in this case is only the mapping to Oeuvre
.
If you want to know whether or not an Oeuvre
is linked to a Livre
or a Magasine
, you can use bi-directional mapping, for example:
@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;
}
Now you can see if oeuvre.getMagasine()
is given or oeuvre.getLivre()
is given.
Upvotes: 1