Ce que j'ai:
@Entity
public class MyEntity {
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
@JoinColumn(name = "myentiy_id")
private List<Address> addreses;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
@JoinColumn(name = "myentiy_id")
private List<Person> persons;
//....
}
public void handle() {
Session session = createNewSession();
MyEntity entity = (MyEntity) session.get(MyEntity.class, entityId);
proceed(session); // FLUSH, COMMIT, CLOSE session!
Utils.objectToJson(entity); //TROUBLES, because it can't convert to json lazy collections
}
Quel problème:
Le problème est que je ne peux pas extraire la collection différée après la fermeture de la session. Mais je ne peux pas non plus ne pas fermer une session dans la méthode continue .
Quelle solution (solution grossière):
a) Avant la fermeture de la session, forcez la mise en veille prolongée à extraire des collections paresseuses
entity.getAddresses().size();
entity.getPersons().size();
....
b) Une manière peut-être plus élégante est d'utiliser l' @Fetch(FetchMode.SUBSELECT)
annotation
Question:
Qu'est-ce qu'une meilleure pratique / une manière courante / une manière plus élégante de le faire? Cela signifie convertir mon objet en JSON.
Vous pouvez parcourir les Getters de l'objet Hibernate dans la même transaction pour vous assurer que tous les objets enfants paresseux sont récupérés avec impatience avec la classe d'assistance générique suivante :
package my.app.util; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashSet; import java.util.Set; import org.aspectj.org.eclipse.jdt.core.dom.Modifier; import org.hibernate.Hibernate; public class HibernateUtil { public static byte[] hibernateCollectionPackage = "org.hibernate.collection".getBytes(); public static void initializeObject( Object o, String insidePackageName ) { Set<Object> seenObjects = new HashSet<Object>(); initializeObject( o, seenObjects, insidePackageName.getBytes() ); seenObjects = null; } private static void initializeObject( Object o, Set<Object> seenObjects, byte[] insidePackageName ) { seenObjects.add( o ); Method[] methods = o.getClass().getMethods(); for ( Method method : methods ) { String methodName = method.getName(); // check Getters exclusively if ( methodName.length() < 3 || !"get".equals( methodName.substring( 0, 3 ) ) ) continue; // Getters without parameters if ( method.getParameterTypes().length > 0 ) continue; int modifiers = method.getModifiers(); // Getters that are public if ( !Modifier.isPublic( modifiers ) ) continue; // but not static if ( Modifier.isStatic( modifiers ) ) continue; try { // Check result of the Getter Object r = method.invoke( o ); if ( r == null ) continue; // prevent cycles if ( seenObjects.contains( r ) ) continue; // ignore simple types, arrays und anonymous classes if ( !isIgnoredType( r.getClass() ) && !r.getClass().isPrimitive() && !r.getClass().isArray() && !r.getClass().isAnonymousClass() ) { // ignore classes out of the given package and out of the hibernate collection // package if ( !isClassInPackage( r.getClass(), insidePackageName ) && !isClassInPackage( r.getClass(), hibernateCollectionPackage ) ) { continue; } // initialize child object Hibernate.initialize( r ); // traverse over the child object initializeObject( r, seenObjects, insidePackageName ); } } catch ( InvocationTargetException e ) { e.printStackTrace(); return; } catch ( IllegalArgumentException e ) { e.printStackTrace(); return; } catch ( IllegalAccessException e ) { e.printStackTrace(); return; } } } private static final Set<Class<?>> IGNORED_TYPES = getIgnoredTypes(); private static boolean isIgnoredType( Class<?> clazz ) { return IGNORED_TYPES.contains( clazz ); } private static Set<Class<?>> getIgnoredTypes() { Set<Class<?>> ret = new HashSet<Class<?>>(); ret.add( Boolean.class ); ret.add( Character.class ); ret.add( Byte.class ); ret.add( Short.class ); ret.add( Integer.class ); ret.add( Long.class ); ret.add( Float.class ); ret.add( Double.class ); ret.add( Void.class ); ret.add( String.class ); ret.add( Class.class ); ret.add( Package.class ); return ret; } private static Boolean isClassInPackage( Class<?> clazz, byte[] insidePackageName ) { Package p = clazz.getPackage(); if ( p == null ) return null; byte[] packageName = p.getName().getBytes(); int lenP = packageName.length; int lenI = insidePackageName.length; if ( lenP < lenI ) return false; for ( int i = 0; i < lenI; i++ ) { if ( packageName[i] != insidePackageName[i] ) return false; } return true; } }
la source
if (object instanceof List) { for(Object item : (List<Object>) object) { initializeObject(item, seenObjects, insidePackageName); } return; } else if (object instanceof Set) { for(Object item : (Set<Object>) object) { initializeObject(item, seenObjects, insidePackageName); } return; }
Iterate lists sinon ignoré.Ce n'est pas la meilleure solution, mais voici ce que j'ai obtenu:
1) Annotez le getter que vous souhaitez initialiser avec cette annotation:
@Retention(RetentionPolicy.RUNTIME) public @interface Lazy { }
2) Utilisez cette méthode (peut être placée dans une classe générique, ou vous pouvez changer T avec la classe Object) sur un objet après l'avoir lu à partir de la base de données:
public <T> void forceLoadLazyCollections(T entity) { Session session = getSession().openSession(); Transaction tx = null; try { tx = session.beginTransaction(); session.refresh(entity); if (entity == null) { throw new RuntimeException("Entity is null!"); } for (Method m : entityClass.getMethods()) { Lazy annotation = m.getAnnotation(Lazy.class); if (annotation != null) { m.setAccessible(true); logger.debug(" method.invoke(obj, arg1, arg2,...); {} field", m.getName()); try { Hibernate.initialize(m.invoke(entity)); } catch (Exception e) { logger.warn("initialization exception", e); } } } } finally { session.close(); } }
la source
Placez le Utils.objectToJson (entité); appeler avant la clôture de la session.
Ou vous pouvez essayer de définir le mode de récupération et de jouer avec un code comme celui-ci
Session s = ... DetachedCriteria dc = DetachedCriteria.forClass(MyEntity.class).add(Expression.idEq(id)); dc.setFetchMode("innerTable", FetchMode.EAGER); Criteria c = dc.getExecutableCriteria(s); MyEntity a = (MyEntity)c.uniqueResult();
la source
Avec Hibernate 4.1.6, une nouvelle fonctionnalité est introduite pour gérer ces problèmes d'association paresseux. Lorsque vous activez la propriété hibernate.enable_lazy_load_no_trans dans hibernate.properties ou dans hibernate.cfg.xml, vous n'aurez plus d'exception LazyInitializationException.
Pour en savoir plus, consultez: https://stackoverflow.com/a/11913404/286588
la source
Lorsque vous devez récupérer plusieurs collections, vous devez:
Hibernate.initialize
pour les collections restantes.Donc, dans votre cas, vous avez besoin d'une première requête JPQL comme celle-ci:
MyEntity entity = session.createQuery("select e from MyEntity e join fetch e.addreses where e.id = :id", MyEntity.class) .setParameter("id", entityId) .getSingleResult(); Hibernate.initialize(entity.persons);
De cette façon, vous pouvez atteindre votre objectif avec 2 requêtes SQL et éviter un produit cartésien.
la source
Hibernate#initialize(entity.getSubSet())
si getSubSet revientCollections.unmodifyableSet(this.subSet)
. J'ai essayé et non. La collection sous-jacente est «PersistentSet». Même histoire avec l'appel#size()
Ce n'est probablement pas une bonne pratique, mais j'appelle généralement un
SIZE
sur la collection pour charger les enfants dans la même transaction, comme vous l'avez suggéré. Il est propre, à l'abri de tout changement dans la structure des éléments enfants et génère du SQL avec une faible surcharge.la source
Essayez d'utiliser la
Gson
bibliothèque pour convertir des objets en JsonExemple avec des servlets:
List<Party> parties = bean.getPartiesByIncidentId(incidentId); String json = ""; try { json = new Gson().toJson(parties); } catch (Exception ex) { ex.printStackTrace(); } response.setContentType("application/json"); response.setCharacterEncoding("UTF-8"); response.getWriter().write(json);
la source
si vous utilisez le référentiel jpa, définissez properties.put ("hibernate.enable_lazy_load_no_trans", true); à jpaPropertymap
la source
Vous pouvez utiliser l'
@NamedEntityGraph
annotation de votre entité pour créer une requête chargeable définissant les collections que vous souhaitez charger sur votre requête.Le principal avantage de ce choix est que hibernate effectue une seule requête pour récupérer l'entité et ses collections et uniquement lorsque vous choisissez d'utiliser ce graphe, comme ceci:
Configuration d'entité
@Entity @NamedEntityGraph(name = "graph.myEntity.addresesAndPersons", attributeNodes = { @NamedAttributeNode(value = "addreses"), @NamedAttributeNode(value = "persons" })
Usage
public MyEntity findNamedGraph(Object id, String namedGraph) { EntityGraph<MyEntity> graph = em.getEntityGraph(namedGraph); Map<String, Object> properties = new HashMap<>(); properties.put("javax.persistence.loadgraph", graph); return em.find(MyEntity.class, id, properties); }
la source
Il y a une sorte de malentendu sur les collections paresseuses dans JPA-Hibernate. Tout d'abord, voyons clairement que pourquoi essayer de lire une collection paresseuse lève des exceptions et pas simplement renvoie NULL pour la conversion ou d'autres cas d'utilisation? .
C'est parce que les champs Null dans les bases de données, en particulier dans les colonnes jointes, ont une signification et pas simplement un état non présenté, comme les langages de programmation. lorsque vous essayez d'interpréter une collection différée à la valeur Null, cela signifie (côté magasin de données) qu'il n'y a pas de relations entre ces entités et ce n'est pas vrai. donc lancer une exception est une sorte de meilleure pratique et vous devez vous en occuper et non avec Hibernate.
Donc, comme mentionné ci-dessus, je recommande de:
également comme décrit dans d'autres réponses, il existe de nombreuses approches (recherche impatiente, participation, etc.) ou bibliothèques et méthodes pour le faire, mais vous devez configurer votre vision de ce qui se passe avant de traiter le problème et de le résoudre.
la source