2010-01-16 12 views
13

Estoy observando un comportamiento muy extraño con una clase de entidad y cargando un objeto de esta clase con JPA (hibernate entitymanager 3.3.1.ga). La clase tiene un campo (incrustado), que se inicializa en la declaración. El colocador del campo implementa una comprobación nula (es decir, arrojaría una excepción cuando se establece un valor nulo).Comportamiento extraño de JPA, el campo inicializado es nulo

... 
@Entity 
public class Participant extends BaseEntity implements Comparable<Participant> { 
    ... 
    @Embedded 
private AmsData amsData = new AmsData(); 

public void setAmsData(AmsData amsData) { 
    Checks.verifyArgNotNull(amsData, "amsdata"); 
    this.amsData = amsData; 
} 
    ... 
} 

Cuando llego a este objeto con JPA, el campo es nulo, si no hay datos en la base de datos para los campos especificados en el objeto incrustado.

... 
public class ParticipantJpaDao implements ParticipantDao { 
@PersistenceContext 
private EntityManager em; 

@Override 
public Participant getParticipant(Long id) { 
    return em.find(Participant.class, id); 
} 
    ... 
} 

que depura el proceso con un punto de observación en el campo (debe detener cuando se accede o se modifica el campo), y veo una modificación cuando se inicializa el campo, pero cuando llegue el resultado de la llamada a find , el campo es nulo.

¿Alguien puede explicar por qué esto es así? ¿Cómo puedo asegurarme de que el campo no sea nulo? También cuando no haya datos para los campos del objeto incrustado en la base de datos (además de configurarlo manualmente después de la llamada de búsqueda).

+0

podría ser esto debido a sus asociaciones carga diferida motor de persistencia? –

+0

Uso carga lenta, pero este es un campo incrustado, por lo tanto, almacenado en la misma tabla. ¿Existe la posibilidad de anotar esto para ser buscado con entusiasmo? – Dominik

+0

Problema de Hibernate relacionado (tomado de la respuesta eliminada): [HHH-7610: Opción para establecer @Embedded field null cuando todas las columnas son NULL] (https://hibernate.atlassian.net/browse/HHH-7610) – sleske

Respuesta

16

La especificación JPA no dice explícitamente cómo manejar un conjunto de columnas que representan un objeto incrustable que están todas vacías. Podría indicar una referencia nula o una instancia de objeto con todos los campos nulos. Hibernate elige una referencia nula en este caso, aunque otras implementaciones JPA pueden elegir la posterior.

La razón por la que nunca se invoca su setter es porque Hibernate está accediendo a su campo a través de la reflexión, evitando el setter que implementó. Lo hace porque utiliza acceso basado en campos en lugar de acceso basado en propiedades.

La respuesta de Chad proporcionaría la funcionalidad que está buscando, pero hay una advertencia (ver más abajo).

" ... El estado persistente de una entidad se accede por el tiempo de ejecución proveedor de persistencia [1] ya sea a través de acceso de propiedad de estilo JavaBeans o a través de las variables de instancia. Un solo tipo de acceso (campo o propiedad de acceso) se aplica a una jerarquía de entidad. Cuando se utilizan anotaciones, la colocación de las anotaciones de mapeo en cualquiera de las campos persistentes o persistente propiedades de la clase entidad especifica el tipo de acceso como ya sea field- o r basadas en la propiedad de acceso respectivamente ..."[ejb3 persistencia especificación]

así moviendo las anotaciones hasta el colocador, le está diciendo a la APP que desea usado acceso basado en la propiedad en lugar de field- acceso basado Sin embargo, debe saber que el acceso basado en el campo, tal como lo implementa actualmente, es preferible al acceso basado en la propiedad. Hay un par de razones por las que se desaconseja el acceso basado en propiedades, pero una es que se ven obligados a agregar getters y setters para todos sus campos de entidad persistentes, pero es posible que no desee que esos mismos campos sean susceptibles de mutación por parte de clientes externos. En otras palabras, usar el acceso basado en propiedades de JPA te obliga a debilitar la encapsulación de tu entidad.

+0

pero ¿por qué? establece el campo a nulo? – Dominik

+0

Actualicé la respuesta para explicar por qué el campo podría ser nulo. La razón más común es que tiene registros en su base de datos que preceden a la adición de su campo ** amsData **. – rcampbell

+0

Acabo de almacenar una entidad nueva con un objeto AmsData no nulo (pero vacío) y obtengo el mismo resultado de nuevo. Y sí, la clase AmsData está incrustada anotada. – Dominik

1

Bueno, es posible que su objeto pueda ser construido dos veces detrás de las escenas. Las implementaciones de JPA generalmente establecerán esos campos directamente.

Creo que necesitas poner las anotaciones en los Getters y en los setters si quieres que se usen. Ver esta respuesta:

Empty constructors and setters on JPA Entites

+0

El objeto incrustado también tiene solo columnas y ninguna asociación, que podría obtenerse con entusiasmo. – Dominik

3

La respuesta es (gracias a rcampell), si todos los datos de un objeto incrustado es nula (en el PP), el objeto incrustado también será nulo, aunque cuando se inicializa en la declaración. La única solución parece ser establecer el objeto manualmente.

@Override 
public Participant getParticipant(Long id) { 
    Participant participant = em.find(Participant.class, id); 
    if(participant != null && participant.getAmsData() == null) 
    { 
     participant.setAmsData(new AmsData()); 
    } 
    return participant; 
} 

todavía se siente extraño para mí ...

+0

Tuve el mismo problema hace algunas semanas. Eso hará el truco. +1 – whiskeysierra

+0

Esta solución activa las instrucciones de actualización sql en el servidor de la base de datos, lo que trae problemas como concurrencia entre subprocesos y problemas de rendimiento. –