tutorial mvc mediante libro framework espaƱol ejemplo desarrollo book aplicaciones java hibernate jpa

java - mediante - spring mvc



Hibernate uno-a-uno: getId() sin recuperar todo el objeto (9)

Ahora hay una biblioteca de tipos de datos de hibernación de jackson aquí:

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

Y puede configurar las características:

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 de carga lenta

Quiero buscar el ID de una relación de uno a uno sin cargar todo el objeto. Pensé que podría hacer esto usando la carga diferida de la siguiente manera:

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 que f.getBar () no active otra búsqueda. Quiero que hibernate me proporcione un objeto proxy que me permita llamar a .getId () sin buscar realmente el objeto Bar.

¿Qué estoy haciendo mal?


En org.hibernate.Session tiene una función que hace el trabajo sin cargar la entidad de forma perezosa:

public Serializable getIdentifier (Object object) arroja HibernateException;

Encontrado en Hibernate 3.3.2.GA:

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(); } }


Java Persistence con Hibernate Book lo menciona en "13.1.3 Understanding Proxies":

Siempre que acceda solo a la propiedad del identificador de la base de datos, no es necesaria la inicialización del proxy. (Tenga en cuenta que esto no es cierto si asigna la propiedad del identificador con acceso directo al campo; Hibernate entonces ni siquiera sabe que existe el método getId (). Si lo llama, el proxy debe inicializarse).

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

En lugar de:

public long getId() { return id; }

Utilizar:

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

La idea aquí es marcar el método getId() como final , para que los proxies no puedan 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.


Lamentablemente, la respuesta aceptada es incorrecta. También otras respuestas no proporcionan la solución más simple o clara.

Use el Nivel de acceso a la propiedad para el ID de la clase BAR .

@Entity public class Bar { @Id @Access(AccessType.PROPERTY) private Long id; ... }

Tan simple como eso :)


Podría usar 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?


Solo para agregar a Arthur Ronald FD Garcia''post: puede forzar el acceso a la propiedad mediante @Access(AccessType.PROPERTY) (o obsoleto @AccessType("property") ), consulte 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 separadas, también.


Usar la estrategia de acceso a la propiedad

En lugar de

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

Utilizar

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 getter del identificador . Pero solo funciona cuando se usa la estrategia de acceso a la propiedad. Tenlo en mente.

Consulte: Guía del usuario de Hibernate 5.2


agregar @AccessType ("propiedad")

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


cambie su método getter así:

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; }