He estado trabajando con JPA (implementación de Hibernate) durante algún tiempo y cada vez que necesito crear entidades me encuentro luchando con problemas como AccessType, propiedades inmutables, equals / hashCode, ....
Así que decidí intentar encontrar las mejores prácticas generales para cada problema y escribir esto para uso personal.
Sin embargo, no me importaría que nadie lo comentara o me dijera dónde me equivoco.
Clase de entidad
implementar serializable
Motivo: La especificación dice que tiene que hacerlo, pero algunos proveedores de JPA no lo hacen cumplir. Hibernate como proveedor de JPA no aplica esto, pero puede fallar en algún lugar profundo de su estómago con ClassCastException, si no se ha implementado Serializable.
Constructores
crear un constructor con todos los campos obligatorios de la entidad
Motivo: Un constructor siempre debe dejar la instancia creada en un estado sano.
además de este constructor: tener un paquete constructor privado predeterminado
Motivo: el constructor predeterminado es necesario para que Hibernate inicialice la entidad; se permite la privacidad, pero se requiere la visibilidad del paquete privado (o público) para la generación de proxy en tiempo de ejecución y la recuperación eficiente de datos sin instrumentación de código de bytes.
Campos / Propiedades
Use el acceso de campo en general y el acceso a la propiedad cuando sea necesario
Motivo: este es probablemente el tema más discutible ya que no hay argumentos claros y convincentes para uno u otro (acceso a la propiedad frente al acceso al campo); sin embargo, el acceso a los campos parece ser el favorito general debido a un código más claro, una mejor encapsulación y sin necesidad de crear setters para campos inmutables
Omitir configuradores para campos inmutables (no es necesario para el campo de tipo de acceso)
- las propiedades pueden ser privadas
Motivo: una vez escuché que protegido es mejor para el rendimiento (Hibernate) pero todo lo que puedo encontrar en la web es: Hibernate puede acceder a métodos de acceso públicos, privados y protegidos, así como a campos públicos, privados y protegidos directamente . La elección depende de usted y puede combinarla para adaptarse al diseño de su aplicación.
Igual / hashCode
- Nunca use una identificación generada si esta identificación solo se establece cuando persiste la entidad
- Por preferencia: use valores inmutables para formar una clave comercial única y úsela para probar la igualdad
- si no hay una Clave comercial única disponible, use un UUID no transitorio que se crea cuando se inicializa la entidad; Vea este gran artículo para más información.
- nunca se refiera a entidades relacionadas (ManyToOne); Si esta entidad (como una entidad matriz) necesita ser parte de la clave comercial, compare solo los ID. Llamar a getId () en un proxy no activará la carga de la entidad, siempre que esté utilizando el tipo de acceso a la propiedad .
Entidad de ejemplo
@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
}
Otras sugerencias para agregar a esta lista son más que bienvenidas ...
ACTUALIZAR
Desde que leí este artículo, adapté mi forma de implementar eq / hC:
- si hay disponible una clave comercial simple inmutable: utilícela
- en todos los demás casos: use un líquido
final
(a juzgar por su omisión de establecedores, supongo que usted también).
notNull
viene