java Crea la entidad JPA perfecta





2 Answers

Intentaré responder varios puntos clave: esto se debe a una larga experiencia de Hibernación / persistencia que incluye varias aplicaciones importantes.

Clase de entidad: implementar Serializable?

Claves necesita implementar Serializable. Las cosas que se incluirán en HttpSession, o que se enviarán por cable mediante RPC / Java EE, deben implementar Serializable. Otras cosas: no tanto. Pasa tu tiempo en lo que es importante.

Constructores: crear un constructor con todos los campos requeridos de la entidad?

El (los) constructor (es) para la lógica de la aplicación, deben tener solo unos pocos campos críticos de "clave externa" o "tipo / clase" que siempre se conocerán al crear la entidad. El resto debe establecerse llamando a los métodos de establecimiento, para eso son.

Evite poner demasiados campos en los constructores. Los constructores deben ser convenientes y darles una cordura básica al objeto. Nombre, Tipo y / o Padres son típicamente útiles.

OTOH si las reglas de aplicación (hoy) requieren que un Cliente tenga una Dirección, déjela a un configurador. Ese es un ejemplo de una "regla débil". Tal vez la próxima semana, ¿desea crear un objeto de Cliente antes de ir a la pantalla Ingresar detalles? No se confunda, deje la posibilidad de datos desconocidos, incompletos o "parcialmente ingresados".

Constructores: también, paquete constructor privado por defecto?

Sí, pero use 'protegido' en lugar de un paquete privado. Subclasificar cosas es un verdadero dolor cuando los elementos internos necesarios no son visibles.

Campos / Propiedades

Utilice el acceso al campo 'propiedad' para Hibernate y desde fuera de la instancia. Dentro de la instancia, use los campos directamente. Motivo: permite que la reflexión estándar, el método más simple y básico para Hibernar, funcione.

En cuanto a los campos 'inmutables' a la aplicación, Hibernate aún necesita poder cargarlos. Puede intentar que estos métodos sean 'privados' y / o poner una anotación en ellos, para evitar que el código de la aplicación haga un acceso no deseado.

Nota: al escribir una función equals (), ¡use captadores para los valores en la 'otra' instancia! De lo contrario, llegará a los campos sin inicializar / vacíos en las instancias de proxy.

¿Protegido es mejor para el rendimiento (hibernación)?

Improbable.

Equals / HashCode?

Esto es relevante para trabajar con entidades, antes de que se hayan guardado, lo cual es un tema espinoso. Hashing / comparando en valores inmutables? En la mayoría de las aplicaciones de negocios, no hay ninguna.

Un cliente puede cambiar la dirección, cambiar el nombre de su negocio, etc., no es común, pero sucede. Las correcciones también deben ser posibles de realizar, cuando los datos no se ingresaron correctamente.

Las pocas cosas que normalmente se mantienen inmutables son la crianza de los hijos y tal vez el tipo / clase: normalmente el usuario recrea el registro, en lugar de cambiarlo. ¡Pero estos no identifican únicamente a la entidad!

Por lo tanto, largos y cortos, los datos "inmutables" reclamados no son realmente. Los campos de clave / ID primarios se generan con el propósito preciso de proporcionar dicha estabilidad e inmutabilidad garantizadas.

Debe planificar y considerar su necesidad de fases de trabajo de comparación, hash y procesamiento de solicitudes cuando A) trabaje con "datos modificados / enlazados" desde la interfaz de usuario si compara / hash en "campos con poca frecuencia", o B) trabajando con " datos no guardados ", si compara / hash en ID.

Equals / HashCode: si una clave de negocio única no está disponible, use un UUID no transitorio que se crea cuando se inicializa la entidad

Sí, esta es una buena estrategia cuando sea necesario. Tenga en cuenta que los UUID no son gratuitos, aunque el rendimiento es inteligente, y la agrupación en clústeres complica las cosas.

Equals / HashCode - nunca se refieren a entidades relacionadas

"Si la entidad relacionada (como una entidad principal) debe formar parte de la Clave comercial, a continuación, agregue un campo no insertable y no actualizable para almacenar la identificación principal (con el mismo nombre que ManytoOne JoinColumn) y use esta identificación en la verificación de igualdad "

Suena como un buen consejo.

¡Espero que esto ayude!

java hibernate jpa equals

He estado trabajando con JPA (implementación Hibernate) por algún tiempo y cada vez que necesito crear entidades me encuentro luchando con problemas como AccessType, propiedades inmutables, igual a / hashCode, ....
Así que decidí intentar conocer la mejor práctica general para cada problema y escribirlo para uso personal.
Sin embargo, no me importaría que alguien comente al respecto o me diga dónde me equivoco.

Clase de entidad

  • Implementar Serializable

    Razón: la especificación dice que debes hacerlo, pero algunos proveedores de JPA no hacen cumplir esto. Hibernate como proveedor de JPA no hace cumplir esto, pero puede fallar en algún lugar profundo de su estómago con ClassCastException, si Serializable no se ha implementado.

Constructores

  • Crea un constructor con todos los campos requeridos 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 por defecto privado

    Motivo: se requiere que el constructor predeterminado haga que Hibernate inicialice la entidad; se permite lo privado, pero se requiere visibilidad privada (o pública) del paquete para la generación de proxy en tiempo de ejecución y la recuperación eficiente de datos sin instrumentación de bytecode.

Campos / Propiedades

  • Utilice el acceso al campo en general y el acceso a la propiedad cuando sea necesario

    Razón: este es probablemente el problema 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 en general debido a un código más claro, mejor encapsulación y no es necesario crear definidores para campos inmutables

  • Omitir configuradores para campos inmutables (no requerido para el campo de tipo de acceso)

  • propiedades pueden ser privadas
    Razón: 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 adaptarla para que se ajuste al diseño de su aplicación.

Equals / 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 de negocio única y use esto para probar la igualdad
  • si una clave comercial única no está 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 refieren a entidades relacionadas (ManyToOne); Si esta entidad (como una entidad matriz) debe ser parte de la clave de negocio, compare solo los ID. Llamar a getId () en un proxy no activará la carga de la entidad, siempre y cuando esté utilizando el tipo de acceso a la propiedad .

Ejemplo de entidad

@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 , he adaptado mi forma de implementar eq / hC:

  • Si hay disponible una clave de negocios simple e inmutable: use esa
  • en todos los demás casos: use un uuid



Interfaz de entidad

public interface Entity<I> extends Serializable {

/**
 * @return entity identity
 */
I getId();

/**
 * @return HashCode of entity identity
 */
int identityHashCode();

/**
 * @param other
 *            Other entity
 * @return true if identities of entities are equal
 */
boolean identityEquals(Entity<?> other);
}

Implementación básica para todas las entidades, simplifica implementaciones de Equals / Hashcode:

public abstract class AbstractEntity<I> implements Entity<I> {

@Override
public final boolean identityEquals(Entity<?> other) {
    if (getId() == null) {
        return false;
    }
    return getId().equals(other.getId());
}

@Override
public final int identityHashCode() {
    return new HashCodeBuilder().append(this.getId()).toHashCode();
}

@Override
public final int hashCode() {
    return identityHashCode();
}

@Override
public final boolean equals(final Object o) {
    if (this == o) {
        return true;
    }
    if ((o == null) || (getClass() != o.getClass())) {
        return false;
    }

    return identityEquals((Entity<?>) o);
}

@Override
public String toString() {
    return getClass().getSimpleName() + ": " + identity();
    // OR 
    // return ReflectionToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
}
}

Entidad de la habitación impl:

@Entity
@Table(name = "ROOM")
public class Room extends AbstractEntity<Integer> {

private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@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;
}

public Integer getId(){
    return id;
}

public Building getBuilding() {
    return building;
}

public String getNumber() {
    return number;
}


public void setCapacity(Integer capacity) {
    this.capacity = capacity;
}

//no setters for number, building nor id
}

No veo el punto de comparar la igualdad de entidades según los campos de negocios en todos los casos de Entidades JPA. Eso podría ser más un caso si se piensa que estas entidades JPA son ValueObjects controlados por el dominio, en lugar de entidades controladas por el dominio (para lo que se incluyen estos ejemplos de código).






Related