Esta pregunta está algo relacionada con la pregunta de colocación de anotaciones de Hibernate .
Pero quiero saber cuál es mejor ? ¿Acceso a través de propiedades o acceso a través de campos? ¿Cuales son las ventajas y desventajas de cada uno?
Esta pregunta está algo relacionada con la pregunta de colocación de anotaciones de Hibernate .
Pero quiero saber cuál es mejor ? ¿Acceso a través de propiedades o acceso a través de campos? ¿Cuales son las ventajas y desventajas de cada uno?
Respuestas:
Prefiero los accesores, ya que puedo agregar algo de lógica empresarial a mis accesores cuando lo necesito. Aquí hay un ejemplo:
@Entity
public class Person {
@Column("nickName")
public String getNickName(){
if(this.name != null) return generateFunnyNick(this.name);
else return "John Doe";
}
}
Además, si agrega otras librerías a la mezcla (como una lib de conversión JSON o BeanMapper o Dozer u otra libra de mapeo / clonación de beans basada en las propiedades getter / setter) tendrá la garantía de que la lib está sincronizada con la persistencia manager (ambos usan getter / setter).
Hay argumentos para ambos, pero la mayoría de ellos se derivan de ciertos requisitos del usuario "¿y si necesita agregar lógica para" o "xxxx rompe la encapsulación". Sin embargo, nadie realmente ha comentado sobre la teoría y ha dado un argumento debidamente razonado.
¿Qué hace realmente Hibernate / JPA cuando persiste un objeto? Bueno, persiste el ESTADO del objeto. Eso significa almacenarlo de manera que pueda reproducirse fácilmente.
¿Qué es la encapsulación? Encapsulaciones significa encapsular los datos (o estado) con una interfaz que la aplicación / cliente puede usar para acceder a los datos de manera segura, manteniéndolos consistentes y válidos.
Piense en esto como MS Word. MS Word mantiene un modelo del documento en la memoria: los documentos ESTADO. Presenta una interfaz que el usuario puede usar para modificar el documento: un conjunto de botones, herramientas, comandos de teclado, etc. Sin embargo, cuando elige persistir (Guardar) ese documento, guarda el estado interno, no el conjunto de pulsaciones de teclas y clics del mouse utilizados para generarlo.
Guardar el estado interno del objeto NO interrumpe la encapsulación; de lo contrario, realmente no comprende lo que significa la encapsulación y por qué existe. Es como la serialización de objetos realmente.
Por esta razón, EN LA MAYORÍA DE LOS CASOS, es apropiado persistir en los CAMPOS y no en los ACCESORIOS. Esto significa que un objeto se puede recrear con precisión desde la base de datos exactamente de la manera en que se almacenó. No debería necesitar ninguna validación, porque esto se hizo en el original cuando se creó, y antes de que se almacenara en la base de datos (a menos, ¡Dios no lo quiera, está almacenando datos no válidos en el DB!). Del mismo modo, no debería ser necesario calcular valores, ya que ya se calcularon antes de que se almacenara el objeto. El objeto debe verse exactamente igual que antes de guardarlo. De hecho, al agregar cosas adicionales a los captadores / establecedores, en realidad está aumentando el riesgo de recrear algo que no sea una copia exacta del original.
Por supuesto, esta funcionalidad se agregó por una razón. Puede haber algunos casos de uso válidos para persistir los accesos, sin embargo, generalmente serán raros. Un ejemplo puede ser que desee evitar la persistencia de un valor calculado, aunque es posible que desee hacer la pregunta de por qué no lo calcula a pedido en el captador del valor, o lo inicializa perezosamente en el captador. Personalmente, no puedo pensar en ningún buen caso de uso, y ninguna de las respuestas aquí realmente da una respuesta de "Ingeniería de software".
Prefiero el acceso al campo, porque de esa manera no estoy obligado a proporcionar getter / setter para cada propiedad.
Una encuesta rápida a través de Google sugiere que el acceso de campo es la mayoría (por ejemplo, http://java.dzone.com/tips/12-feb-jpa-20-why-accesstype ).
Creo que el acceso al campo es el idioma recomendado por Spring, pero no puedo encontrar una referencia que lo respalde.
Hay una pregunta SO relacionada que trató de medir el rendimiento y llegó a la conclusión de que "no hay diferencia".
Aquí hay una situación en la que TIENE que usar accesores de propiedad. Imagine que tiene una clase abstracta GENÉRICA con mucha bondad de implementación para heredar en 8 subclases concretas:
public abstract class Foo<T extends Bar> {
T oneThing;
T anotherThing;
// getters and setters ommited for brevity
// Lots and lots of implementation regarding oneThing and anotherThing here
}
¿Ahora exactamente cómo debe anotar esta clase? La respuesta es que NO PUEDE anotarlo en absoluto con acceso a campos o propiedades porque no puede especificar la entidad objetivo en este momento. Tienes que anotar las implementaciones concretas. Pero como las propiedades persistentes se declaran en esta superclase, DEBE utilizar el acceso a la propiedad en las subclases.
El acceso de campo no es una opción en una aplicación con superclases genéricas abstractas.
abstract T getOneThing()
, y abstract void setOneThing(T thing)
, y usar el acceso de campo.
Tiendo a preferir y usar accesores de propiedad:
foo.getId()
sin inicializar un proxy (importante cuando utilizo Hibernate, hasta que se resuelva HHH-3718 ).Retirarse:
@Transient
por ahí.Eso realmente depende de un caso específico: ambas opciones están disponibles por una razón. En mi opinión, se reduce a tres casos:
Recomiendo encarecidamente el acceso al campo y NO las anotaciones en los captadores (acceso a la propiedad) si desea hacer algo más en los establecedores que solo establecer el valor (por ejemplo, Cifrado o cálculo).
El problema con el acceso a la propiedad es que también se llama a los establecedores cuando se carga el objeto. Esto me funcionó bien durante muchos meses hasta que quisimos introducir el cifrado. En nuestro caso de uso, queríamos cifrar un campo en el setter y descifrarlo en el getter. El problema ahora con el acceso a la propiedad era que cuando Hibernate cargaba el objeto, también llamaba al configurador para llenar el campo y, por lo tanto, encriptaba el valor encriptado nuevamente. Esta publicación también menciona esto: Java Hibernate: comportamiento de función de conjunto de propiedades diferente dependiendo de quién lo llame
Esto me ha causado dolores de cabeza hasta que recordé la diferencia entre el acceso al campo y el acceso a la propiedad. Ahora he movido todas mis anotaciones del acceso a la propiedad al acceso al campo y ahora funciona bien.
Prefiero usar el acceso de campo por las siguientes razones:
El acceso a la propiedad puede generar errores muy desagradables al implementar equals / hashCode y hacer referencia a campos directamente (a diferencia de sus captadores). Esto se debe a que el proxy solo se inicializa cuando se accede a los captadores, y un acceso de campo directo simplemente devolvería nulo.
El acceso a la propiedad requiere que anote todos los métodos de utilidad (por ejemplo, addChild / removeChild) como @Transient
.
Con el acceso al campo, podemos ocultar el campo @Version al no exponer un captador en absoluto. Un getter también puede llevar a agregar un setter, y el version
campo nunca debe establecerse manualmente (lo que puede generar problemas muy desagradables). Todo el incremento de la versión debe activarse mediante el bloqueo explícito OPTIMISTIC_FORCE_INCREMENT o PESSIMISTIC_FORCE_INCREMENT .
version
campo a menudo es útil en situaciones en las que se usan DTO en lugar de entidades separadas.
Creo que anotar la propiedad es mejor porque la actualización de los campos directamente interrumpe la encapsulación, incluso cuando su ORM lo hace.
Aquí hay un gran ejemplo de dónde te quemará: probablemente quieras tus anotaciones para el validador de hibernación y la persistencia en el mismo lugar (campos o propiedades). Si desea probar las validaciones activadas por su validador de hibernación que están anotadas en un campo, no puede usar una simulación de su entidad para aislar su prueba de unidad solo para el validador. Ay.
Creo que el acceso a la propiedad frente al acceso al campo es sutilmente diferente con respecto a la inicialización diferida.
Considere las siguientes asignaciones para 2 beans básicos:
<hibernate-mapping package="org.nkl.model" default-access="field">
<class name="FieldBean" table="FIELD_BEAN">
<id name="id">
<generator class="sequence" />
</id>
<property name="message" />
</class>
</hibernate-mapping>
<hibernate-mapping package="org.nkl.model" default-access="property">
<class name="PropBean" table="PROP_BEAN">
<id name="id">
<generator class="sequence" />
</id>
<property name="message" />
</class>
</hibernate-mapping>
Y las siguientes pruebas unitarias:
@Test
public void testFieldBean() {
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
FieldBean fb = new FieldBean("field");
Long id = (Long) session.save(fb);
tx.commit();
session.close();
session = sessionFactory.openSession();
tx = session.beginTransaction();
fb = (FieldBean) session.load(FieldBean.class, id);
System.out.println(fb.getId());
tx.commit();
session.close();
}
@Test
public void testPropBean() {
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
PropBean pb = new PropBean("prop");
Long id = (Long) session.save(pb);
tx.commit();
session.close();
session = sessionFactory.openSession();
tx = session.beginTransaction();
pb = (PropBean) session.load(PropBean.class, id);
System.out.println(pb.getId());
tx.commit();
session.close();
}
Verá la sutil diferencia en las selecciones requeridas:
Hibernate:
call next value for hibernate_sequence
Hibernate:
insert
into
FIELD_BEAN
(message, id)
values
(?, ?)
Hibernate:
select
fieldbean0_.id as id1_0_,
fieldbean0_.message as message1_0_
from
FIELD_BEAN fieldbean0_
where
fieldbean0_.id=?
0
Hibernate:
call next value for hibernate_sequence
Hibernate:
insert
into
PROP_BEAN
(message, id)
values
(?, ?)
1
Es decir, llamar fb.getId()
requiere una selección, mientras pb.getId()
que no.
Permítanme tratar de resumir las razones más importantes para elegir el acceso basado en el campo. Si desea profundizar más, lea este artículo en mi blog: Estrategias de acceso en JPA e Hibernate: ¿cuál es mejor, el acceso al campo o a la propiedad?
El acceso basado en el campo es, con mucho, la mejor opción. Aquí hay 5 razones para ello:
Razón 1: mejor legibilidad de su código
Si utiliza el acceso basado en campos, anota los atributos de su entidad con sus anotaciones de mapeo. Al colocar la definición de todos los atributos de entidad en la parte superior de su clase, obtiene una vista relativamente compacta de todos los atributos y sus asignaciones.
Razón 2: Omita los métodos getter o setter que su aplicación no debería invocar
Otra ventaja del acceso basado en el campo es que su proveedor de persistencia, por ejemplo, Hibernate o EclipseLink, no utiliza los métodos getter y setter de los atributos de su entidad. Eso significa que no necesita proporcionar ningún método que su código comercial no deba utilizar. Este suele ser el caso de los métodos de establecimiento de atributos de clave primaria generados o columnas de versión. Su proveedor de persistencia gestiona los valores de estos atributos, y no debe establecerlos mediante programación.
Razón 3: implementación flexible de los métodos getter y setter
Debido a que su proveedor de persistencia no llama a los métodos getter y setter, no está obligado a cumplir ningún requisito externo. Puede implementar estos métodos de la forma que desee. Eso le permite implementar reglas de validación específicas del negocio, activar una lógica comercial adicional o convertir el atributo de la entidad en un tipo de datos diferente.
Puede, por ejemplo, usar eso para envolver una asociación o atributo opcional en un Java Optional
.
Razón 4: no es necesario marcar los métodos de utilidad como @Transient
Otro beneficio de la estrategia de acceso basado en el campo es que no necesita anotar sus métodos de utilidad @Transient
. Esta anotación le dice a su proveedor de persistencia que un método o atributo no es parte del estado persistente de la entidad. Y debido a que con el acceso de tipo campo, el estado persistente se define por los atributos de su entidad, su implementación JPA ignora todos los métodos de su entidad.
Razón 5: evitar errores al trabajar con proxies
Hibernate usa proxies para asociaciones uno-vagamente obtenidas para que pueda controlar la inicialización de estas asociaciones. Ese enfoque funciona bien en casi todas las situaciones. Pero presenta un peligro peligroso si utiliza el acceso basado en la propiedad.
Si usa acceso basado en propiedades, Hibernate inicializa los atributos del objeto proxy cuando llama al método getter. Ese es siempre el caso si usa el objeto proxy en su código comercial. Pero bastante implementaciones igual y hashCode acceden a los atributos directamente. Si es la primera vez que accede a alguno de los atributos proxy, estos atributos aún no se han inicializado.
De forma predeterminada, los proveedores de JPA acceden a los valores de los campos de entidad y asignan esos campos a las columnas de la base de datos utilizando los métodos de acceso de propiedad JavaBean (getter) y de mutador (setter) de la entidad. Como tal, los nombres y tipos de los campos privados en una entidad no son importantes para JPA. En cambio, JPA solo analiza los nombres y tipos de retorno de los accesores de propiedad JavaBean. Puede modificar esto mediante la @javax.persistence.Access
anotación, que le permite especificar explícitamente la metodología de acceso que debe utilizar el proveedor de JPA.
@Entity
@Access(AccessType.FIELD)
public class SomeEntity implements Serializable
{
...
}
Las opciones disponibles para la enumeración AccessType son PROPIEDAD (el valor predeterminado) y CAMPO. Con PROPIEDAD, el proveedor obtiene y establece valores de campo utilizando los métodos de propiedad JavaBean. FIELD hace que el proveedor obtenga y establezca valores de campo utilizando los campos de instancia. Como práctica recomendada, debe seguir con el valor predeterminado y usar las propiedades JavaBean a menos que tenga una razón convincente para hacerlo de otra manera.
Puede colocar estas anotaciones de propiedad en los campos privados o en los métodos de acceso público. Si usa AccessType.PROPERTY
(predeterminado) y anota los campos privados en lugar de los accesores JavaBean, los nombres de los campos deben coincidir con los nombres de las propiedades JavaBean. Sin embargo, los nombres no tienen que coincidir si anota los accesores JavaBean. Del mismo modo, si usa AccessType.FIELD
y anota los accesores JavaBean en lugar de los campos, los nombres de los campos también deben coincidir con los nombres de las propiedades JavaBean. En este caso, no tienen que coincidir si anota los campos. Es mejor ser coherente y anotar los accesores JavaBean AccessType.PROPERTY
y los campos para
AccessType.FIELD
.
Es importante que nunca mezcle anotaciones de propiedad JPA y anotaciones de campo JPA en la misma entidad. Hacerlo da como resultado un comportamiento no especificado y es muy probable que cause errores.
Esa es una presentación antigua, pero Rod sugiere que las anotaciones sobre el acceso a la propiedad alientan los modelos de dominio anémico y no deberían ser la forma "predeterminada" de anotar.
Otro punto a favor del acceso al campo es que, de lo contrario, se verá obligado a exponer los establecedores para las colecciones, lo que, para mí, es una mala idea, ya que cambiar la instancia de colección persistente a un objeto no administrado por Hibernate definitivamente romperá la consistencia de sus datos.
Por lo tanto, prefiero tener colecciones como campos protegidos inicializados para implementaciones vacías en el constructor predeterminado y exponer solo sus captadores. Las operaciones a continuación, sólo se maneja como clear()
, remove()
, removeAll()
es posible que nunca se hará Hibernate conscientes de los cambios, etc.
Prefiero los campos, pero me he encontrado con una situación que parece obligarme a colocar las anotaciones en los captadores.
Con la implementación Hibernate JPA, @Embedded
no parece funcionar en los campos. Así que eso tiene que irse por sorpresa. Y una vez que lo pones en el captador, las diversas @Column
anotaciones también tienen que ir a los captadores . (Creo que Hibernate no quiere mezclar campos y captadores aquí.) Y una vez que se ponen @Column
captadores en una clase, probablemente tenga sentido hacerlo todo el tiempo.
Estoy a favor de los accesores de campo. El código es mucho más limpio. Todas las anotaciones se pueden colocar en una sección de una clase y el código es mucho más fácil de leer.
Encontré otro problema con los accesores de propiedades: si tiene métodos getXYZ en su clase que NO están anotados como asociados con propiedades persistentes, hibernate genera sql para intentar obtener esas propiedades, lo que resulta en algunos mensajes de error muy confusos. Dos horas desperdiciadas. No escribí este código; Siempre he usado accesores de campo en el pasado y nunca me he encontrado con este problema.
Versiones de Hibernate utilizadas en esta aplicación:
<!-- hibernate -->
<hibernate-core.version>3.3.2.GA</hibernate-core.version>
<hibernate-annotations.version>3.4.0.GA</hibernate-annotations.version>
<hibernate-commons-annotations.version>3.1.0.GA</hibernate-commons-annotations.version>
<hibernate-entitymanager.version>3.4.0.GA</hibernate-entitymanager.version>
Debe elegir el acceso a través de campos sobre el acceso a través de propiedades. Con los campos puede limitar los datos enviados y recibidos. Con las propiedades via, puede enviar más datos como host y establecer denominaciones G (que configuran de fábrica la mayoría de las propiedades en total).
Tenía la misma pregunta sobre accesstype en hibernate y encontré algunas respuestas aquí .
He resuelto la inicialización perezosa y el acceso al campo aquí Hibernate one-to-one: getId () sin recuperar el objeto completo
Creamos beans de entidad y utilizamos anotaciones getter. El problema con el que nos encontramos es el siguiente: algunas entidades tienen reglas complejas para algunas propiedades con respecto a cuándo se pueden actualizar. La solución fue tener cierta lógica de negocios en cada setter que determina si el valor real cambió o no y, de ser así, si el cambio debería permitirse. Por supuesto, Hibernate siempre puede establecer las propiedades, por lo que terminamos con dos grupos de establecedores. Muy feo.
Al leer publicaciones anteriores, también veo que hacer referencia a las propiedades desde el interior de la entidad podría generar problemas con las colecciones que no se cargan.
En pocas palabras, me inclinaría hacia la anotación de los campos en el futuro.
Normalmente los frijoles son POJO, por lo que tienen accesores de todos modos.
Entonces, la pregunta no es "¿cuál es mejor?", Sino simplemente "¿cuándo usar el acceso de campo?". Y la respuesta es "cuando no necesita un setter / getter para el campo!".
Estoy pensando en esto y elijo el método de acceso
¿por qué?
porque el campo y el acceso al método son los mismos, pero si luego necesito algo de lógica en el campo de carga, guardo mover todas las anotaciones colocadas en los campos
Saludos
Grubhart
Ambos :
La especificación EJB3 requiere que declare anotaciones sobre el tipo de elemento al que se accederá, es decir, el método getter si usa el acceso a la propiedad, el campo si usa el acceso al campo.
https://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/#entity-mapping
AccessType.PROPERTY: la implementación de persistencia EJB cargará el estado en su clase a través de métodos "setter" de JavaBean y recuperará el estado de su clase usando métodos "getter" de JavaBean. Este es el valor predeterminado.
AccessType.FIELD: el estado se carga y se recupera directamente de los campos de su clase. No tiene que escribir JavaBean "getters" y "setters".