2010-04-07 13 views
48

Quiero buscar el ID de una relación uno-a-uno sin cargar todo el objeto. Pensé que podía hacer esto utilizando la carga diferida de la siguiente manera:Hibernate uno-a-uno: getId() sin obtener todo el objeto

class Foo { 
    @OneToOne(fetch = FetchType.LAZY, optional = false) 
    private Bar bar; 
} 


Foo f = session.get(Foo.class, fooId); // Hibernate fetches Foo 

f.getBar(); // Hibernate fetches full Bar object 

f.getBar().getId(); // No further fetch, returns id 

Quiero f.getBar() para no gatillo otra exploración. Quiero que hibernate me proporcione un objeto proxy que me permita llamar a .getId() sin buscar realmente el objeto Bar.

¿Qué estoy haciendo mal?

+0

Mismo comportamiento usando @ManyToOne (fetch = FetchType.LAZY, opcionales = false) asociaciones de un solo valor son simplemente no va bien para mí .. – Rob

+0

Es un error de Hibernate: https://hibernate.atlassian.net/browse/HHH-3718 Ver también comparando el acceso de campo o propiedad: http://stackoverflow.com/questions/594597/hibernate-annotations-which-is- mejor acceso de campo o propiedad – GKislin

Respuesta

29

Uso estrategia de acceso a la propiedad

En lugar de

@OneToOne(fetch=FetchType.LAZY, optional=false) 
private Bar bar; 

Uso

private Bar bar; 

@OneToOne(fetch=FetchType.LAZY, optional=false) 
public Bar getBar() { 
    return this.bar; 
} 

Ahora funciona bien!

Un proxy se inicializa si llama a cualquier método que no sea el método de obtención de identificador. Pero solo funciona cuando se usa la estrategia de acceso a la propiedad. Tenlo en mente.

Ver: Hibernate 5.2 user guide

+0

¿Eso significa que tengo que cambiar mi entidad para tener todas las anotaciones en el nivel de propiedad para que esto funcione? Si salgo como está y muevo la anotación de uno a uno al nivel de propiedad y establezco el tipo de acceso a la propiedad, no funciona –

+0

@shane lee Ver http://docs.jboss.org/ejb3/app-server/HibernateAnnotations/reference/es/html_single/# d0e1955 –

+1

Gracias por la respuesta. No confiaba en que la consulta se ejecutara en mi proyecto, así que agregué en mi propia prueba de integración de db para verificar. Tengo que trabajar ahora. El único cambio es agregar el tipo de acceso en la identificación de la entidad objetivo. Ese fue el único cambio necesario. @Id @GeneratedValue ( estrategia = GenerationType.SEQUENCE, generador = "FILECONTENT_ID_SEQ") @SequenceGenerator ( name = "FILECONTENT_ID_SEQ", sequenceName = "FILECONTENT_ID_SEQ") @Column ( name = "ID", nullable = false) @Access (AccessType.PROPERTY) private Long id; –

0

Puede utilizar una consulta HQL. El método getBar() realmente devolverá un proxy, que no será recuperado hasta que invoque algún método vinculado a datos. No estoy seguro de cuál es exactamente tu problema. ¿Puedes darnos más antecedentes?

+1

Gracias por la respuesta. Lo que describes no es lo que sucede. getBar() está causando que ocurra la recuperación. Esperaría lo que describes, que se devuelve un objeto proxy y no se ejecuta ninguna búsqueda. ¿Hay alguna otra configuración que me podría estar faltando? – Rob

+0

En realidad, el getId() que sigue a getBar() está causando que la entidad sea captada. No te falta ninguna configuración de IMO. Tal vez alguna consulta como "seleccione f.bar.id de Foo f donde f.id =?" hará el truco para ti. –

+0

El objeto proxy no debe obtener la barra completa en bar.getId(). Ya conoce la identificación, ya que es parte de Foo. De todos modos, ejecuta la búsqueda sin invocar.getId() – Rob

22

Sólo para añadir a la Arthur Ronald FD Garcia'post: puede forzar acceso a la propiedad @Access(AccessType.PROPERTY) (o desaprobado @AccessType("property")), ver http://256stuff.com/gray/docs/misc/hibernate_lazy_field_access_annotations.shtml

Otra solución puede ser:

public static Integer getIdDirect(Entity entity) { 
    if (entity instanceof HibernateProxy) { 
     LazyInitializer lazyInitializer = ((HibernateProxy) entity).getHibernateLazyInitializer(); 
     if (lazyInitializer.isUninitialized()) { 
      return (Integer) lazyInitializer.getIdentifier(); 
     } 
    } 
    return entity.getId(); 
} 

Funciona para entidades independientes, también.

+1

He utilizado su idea, más el hecho de que los proxies no pueden anular los métodos finales, para alterar el método 'getId()' para evitar la inicialización. Por favor, si puedes, mira mi respuesta en esta página y dime lo que piensas. Tampoco entiendo por qué está comprobando si 'lazyInitializer.isUninitialized()'. ¿No puedes devolver siempre 'lazyInitializer.getIdentifier()' cuando la entidad es un HibernateProxy? – MarcG

+0

No recuerdo por qué he usado 'if (lazyInitializer.isUninitialized())'. Tal vez para usar el truco sucio solo cuando sea realmente necesario. Creo que puede ser omitido. – xmedeko

+1

La anotación de Hibernate "AccessType" está en desuso. Utilice la anotación JPA2 en su lugar '@Access (AccessType.PROPERTY)' – minni

3

En org.hibernate.Session tiene una función que realizan el trabajo sin carga diferida la entidad:

pública Serializable getIdentifier (objeto Object) lanza HibernateException;

encontrados en 3.3.2.GA de hibernación:

public Serializable getIdentifier(Object object) throws HibernateException { 
     errorIfClosed(); 
     checkTransactionSynchStatus(); 
     if (object instanceof HibernateProxy) { 
      LazyInitializer li = ((HibernateProxy) object).getHibernateLazyInitializer(); 
      if (li.getSession() != this) { 
       throw new TransientObjectException("The proxy was not associated with this session"); 
      } 
      return li.getIdentifier(); 
     } 
     else { 
      EntityEntry entry = persistenceContext.getEntry(object); 
      if (entry == null) { 
       throw new TransientObjectException("The instance was not associated with this session"); 
      } 
      return entry.getId(); 
     } 
    } 
+0

+1. Simplemente no funciona sin la sesión, p. para entidades separadas – xmedeko

7

La persistencia de Java con Hibernate libro menciona esto en "13.1.3 La representación entendimiento":

Mientras se accede sólo el propiedad de identificador de base de datos, no inicialización del proxy es necesario. (Tenga en cuenta que esto no es verdadero si mapea la propiedad del identificador con acceso directo al campo; Hibernate entonces ni siquiera sabe que el método getId() existe. Si lo llama, el proxy debe ser inicializado.)

Sin embargo, en base a @xmedeko respuesta en esta página he desarrollado un truco para evitar inicializar el proxy incluso cuando se utiliza la estrategia de acceso directo campo. Simplemente modifique el método getId() como se muestra a continuación.

En lugar de:

public long getId() { return id; } 

Uso:

public final long getId() { 
     if (this instanceof HibernateProxy) { 
      return (long)((HibernateProxy)this).getHibernateLazyInitializer().getIdentifier(); 
     } 
     else { return id; } 
    } 

La idea aquí es para marcar el método getId() como final, por lo que los proxies no pueden anularlo. Entonces, llamar al método no puede ejecutar ningún código proxy y, por lo tanto, no puede inicializar el proxy. El método mismo verifica si su instancia es un proxy, y en este caso devuelve el id del proxy. Si la instancia es el objeto real, devuelve la identificación.

+0

Jaja esto es realmente un hack horrible :) "No lo hagas en casa" –

+0

@ OndraŽižka Estás equivocado. Este código funciona perfectamente. Además, no rompe ninguna regla, no hay efectos secundarios, y está claro lo que está haciendo y por qué. Entonces, si puede pensar en alguna razón para no usar este código o por qué es "horrible", por favor comparta. – MarcG

+0

Va a las clases internas de Hibernate, que puede cambiar sin previo aviso. Si bien no dudo de que funcione a la perfección, no es algo que pondría en una aplicación que se supone que durará años. –

0

cambiar su método de obtención de esta manera:

public Bar getBar() { 
    if (bar instanceof HibernateProxy) { 
     HibernateProxy hibernateProxy = (HibernateProxy) this.bar; 
     LazyInitializer lazyInitializer = hibernateProxy.getHibernateLazyInitializer(); 
     if (lazyInitializer.getSession() == null) 
      bar = new Bar((long) lazyInitializer.getIdentifier()); 
    } 

    return bar; 
} 
10

añadir @AccessType ("propiedad")

@Id 
@GeneratedValue(strategy = GenerationType.IDENTITY) 
@AccessType("property") 
protected Long id; 
+10

La anotación de Hibernate "AccessType" está en desuso. Use la anotación JPA2 en su lugar: '@Access (AccessType.PROPERTY)' – minni

1

ahora hay una biblioteca de tipo de datos de hibernación Jackson aquí:

https://github.com/FasterXML/jackson-datatype-hibernate

Y usted c una configurar las funciones de:

Hibernate4Module hibernate4Module = new Hibernate4Module(); 
hibernate4Module.configure(Hibernate4Module.Feature.SERIALIZE_IDENTIFIER_FOR_LAZY_NOT_LOADED_OBJECTS, true); 

Esto incluirá la identificación de la relación-cargado perezoso

5

Por desgracia, la respuesta aceptada es erróneo. También otras respuestas no proporcionan la solución más simple o clara.

Utilice el Nivel de acceso de propiedad para ID de la clase BAR.

@Entity 
public class Bar { 

    @Id 
    @Access(AccessType.PROPERTY) 
    private Long id; 

    ... 
} 

tan simple como que :)

Cuestiones relacionadas