Hibernate: best practice to pull out all lazy collections

What I have:

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

What a problem:

The problem is that I cannot get a lazy collection after closing the session. But I also can’t close the session in the continue method.

Which solution (rude decision):

a) Before closing the session, make sleep mode pull lazy collections

 entity.getAddresses().size(); entity.getPersons().size(); 

....

b) Perhaps a more attractive way is to use the @Fetch(FetchMode.SUBSELECT) annotation @Fetch(FetchMode.SUBSELECT)

Question:

What is the best practice / general way / more flexible way to do this? The tool converts my object to JSON.

+77
java hibernate lazy-loading
Nov 12 '13 at 11:59
source share
9 answers

Use Hibernate.initialize() inside @Transactional to initialize lazy objects.

  start Transaction Hibernate.initialize(entity.getAddresses()); Hibernate.initialize(entity.getPersons()); end Transaction 

Now on the transaction side you can get lazy objects.

 entity.getAddresses().size(); entity.getPersons().size(); 
+96
Nov 12 '13 at 12:07
source share

You can navigate through the Getters of Hibernate object in the same transaction to ensure that all lazy child objects look forward to the following general helper class:

HibernateUtil.initializeObject (myObject, "my.app.model");

 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; } } 
+7
Jul 21 '14 at 17:00
source share

Not the best solution, but here is what I got:

1) Annotate the getter that you want to initialize with this annotation:

 @Retention(RetentionPolicy.RUNTIME) public @interface Lazy { } 

2) Use this method (you can put it in a general class or change T with the Object class) to an object after reading it from the database:

  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(); } } 
+6
Jun 10 '16 at 1:35
source share

Put Utils.objectToJson (entity); call before closing the session.

Or you can try to set the fetch mode and play with code like this

 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(); 
+4
Nov 12 '13 at 12:08
source share

Hibernate 4.1.6 introduces a new feature to solve these problems with lazy associations. When you enable the hibernate.enable_lazy_load_no_trans property in the hibernate.properties file or in the hibernate.cfg.xml file, you will no longer have a LazyInitializationException.

See https://stackoverflow.com/a/3776953/ for more information.

+4
Mar 17 '15 at 3:28
source share

It most likely does not fit any best practice, but I usually call SIZE in the collection to load the children into the same transaction, as you said. It is clean, immune to any changes in the structure of children, and provides SQL with low overhead.

+2
Nov 12 '13 at 12:06 on
source share

Try using the Gson library to convert objects to Json

Servlet example:

  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); 
0
Nov 13 '17 at 8:53 on
source share

When you need to get several collections, you need:

  1. JOIN one collection
  2. Use Hibernate.initialize for the rest of the collections.

So, in your case, you need the first JPQL query, like this one:

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

Thus, you can achieve your goal with two SQL queries and avoid Cartesian products.

0
Dec 08 '18 at 17:46
source share

if you are using jpa repository, set properties.put ("hibernate.enable_lazy_load_no_trans", true); in jpaPropertymap

0
Dec 18 '18 at 8:59
source share



All Articles