Je travaille avec JPA (implémentation Hibernate) depuis un certain temps maintenant et chaque fois que j'ai besoin de créer des entités, je me retrouve avec des problèmes comme AccessType, les propriétés immuables, equals / hashCode, ....
J'ai donc décidé d'essayer de trouver la meilleure pratique générale pour chaque problème et de l'écrire pour un usage personnel.
Cela ne me dérangerait cependant pas que quiconque commente ou me dise où je me trompe.
Classe d'entité
implémenter Serializable
Raison: la spécification dit que vous devez le faire, mais certains fournisseurs JPA ne l'appliquent pas. Hibernate en tant que fournisseur JPA n'applique pas cela, mais il peut échouer quelque part au fond de son estomac avec ClassCastException, si Serializable n'a pas été implémenté.
Constructeurs
créer un constructeur avec tous les champs obligatoires de l'entité
Raison: un constructeur doit toujours laisser l'instance créée dans un état sain.
en plus de ce constructeur: avoir un package constructeur par défaut privé
Motif: le constructeur par défaut doit demander à Hibernate d'initialiser l'entité; private est autorisé mais la visibilité privée (ou publique) du package est requise pour la génération de proxy d'exécution et la récupération efficace des données sans instrumentation de bytecode.
Champs / Propriétés
Utilisez l'accès aux champs en général et l'accès à la propriété en cas de besoin
Raison: c'est probablement la question la plus discutable car il n'y a pas d'arguments clairs et convaincants pour l'un ou l'autre (accès à la propriété vs accès au champ); cependant, l'accès aux champs semble être le favori général en raison d'un code plus clair, d'une meilleure encapsulation et de la nécessité de créer des paramètres pour les champs immuables
Omettre les paramètres pour les champs immuables (non requis pour le champ de type d'accès)
- les propriétés peuvent être privées
Raison: j'ai entendu dire que protégé est meilleur pour les performances (mise en veille prolongée), mais tout ce que je peux trouver sur le Web est: Hibernate peut accéder aux méthodes d'accesseur publiques, privées et protégées, ainsi qu'aux champs publics, privés et protégés directement . Le choix vous appartient et vous pouvez l'adapter à la conception de votre application.
Equals / hashCode
- N'utilisez jamais un identifiant généré si cet identifiant est uniquement défini lors de la persistance de l'entité
- De préférence: utilisez des valeurs immuables pour former une clé métier unique et utilisez-la pour tester l'égalité
- si une clé d'entreprise unique n'est pas disponible, utilisez un UUID non transitoire qui est créé lors de l'initialisation de l'entité; Consultez cet excellent article pour plus d'informations.
- ne faites jamais référence à des entités liées (ManyToOne); si cette entité (comme une entité parente) doit faire partie de la clé d'entreprise, comparez uniquement les ID. L'appel de getId () sur un proxy ne déclenchera pas le chargement de l'entité, tant que vous utilisez le type d'accès à la propriété .
Exemple d'entité
@Entity
@Table(name = "ROOM")
public class Room implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
@Column(name = "room_id")
private Integer id;
@Column(name = "number")
private String number; //immutable
@Column(name = "capacity")
private Integer capacity;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "building_id")
private Building building; //immutable
Room() {
// default constructor
}
public Room(Building building, String number) {
// constructor with required field
notNull(building, "Method called with null parameter (application)");
notNull(number, "Method called with null parameter (name)");
this.building = building;
this.number = number;
}
@Override
public boolean equals(final Object otherObj) {
if ((otherObj == null) || !(otherObj instanceof Room)) {
return false;
}
// a room can be uniquely identified by it's number and the building it belongs to; normally I would use a UUID in any case but this is just to illustrate the usage of getId()
final Room other = (Room) otherObj;
return new EqualsBuilder().append(getNumber(), other.getNumber())
.append(getBuilding().getId(), other.getBuilding().getId())
.isEquals();
//this assumes that Building.id is annotated with @Access(value = AccessType.PROPERTY)
}
public Building getBuilding() {
return building;
}
public Integer getId() {
return id;
}
public String getNumber() {
return number;
}
@Override
public int hashCode() {
return new HashCodeBuilder().append(getNumber()).append(getBuilding().getId()).toHashCode();
}
public void setCapacity(Integer capacity) {
this.capacity = capacity;
}
//no setters for number, building nor id
}
D'autres suggestions à ajouter à cette liste sont plus que bienvenues ...
MISE À JOUR
Depuis la lecture de cet article, j'ai adapté ma façon de mettre en œuvre eq / hC:
- si une clé métier simple immuable est disponible: utilisez-la
- dans tous les autres cas: utilisez un uuid
final
(à en juger par votre omission des setters, je suppose que vous aussi).
notNull
vient-il?