Hibernate:拉取所有懒惰集合的最佳实践

我拥有的:

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

什么问题:

问题是closures会话后,我不能拉懒惰的收集。 但是我也不能不用closures会话的方法。

什么是解决scheme(粗略解决scheme):

a)在会话closures之前,强迫hibernate拉取懒惰的集合

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

….

b)也许更优雅的方法是使用@Fetch(FetchMode.SUBSELECT)注释

题:

什么是最好的做法/常用的方式/更优雅的方式来做到这一点? 手段将我的对象转换为JSON。

使用@Transactional Hibernate.initialize()来初始化lazy对象。

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

现在在事务的外面你可以得到懒惰的对象。

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

你可以遍历同一个事务中的Hibernate对象的Getters,以确保所有的懒子对象都被下面的generics助手类获取:

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

放置Utils.objectToJson(实体); 在会议结束之前致电。

或者你可以尝试设置获取模式,并用这样的代码玩

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

随着Hibernate 4.1.6引入了一个新的function来处理这些懒惰的关联问题。 当您在hibernate.properties或hibernate.cfg.xml中启用hibernate.enable_lazy_load_no_trans属性时,您将不再有LazyInitializationException。

更多请参阅: https : //stackoverflow.com/a/11913404/286588

这可能不是任何接近最佳做法的地方,但是我通常会在集合上调用SIZE来加载同一事务中的子项,就像您所build议的那样。 它很干净,不受子元素结构的任何变化的影响,而且产生的SQL开销很小。

不是最好的解决scheme,但这里是我得到的:

1)用这个注释来注释你想要初始化的getter:

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

2)从数据库读取对象后,使用此方法(可以放在generics类中,也可以使用Object类更改T):

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

尝试使用Gson库将对象转换为Json

使用servlet的示例:

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