首页 文章

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

提问于
浏览
66

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:

问题是我无法在proceed方法中关闭会话 .

What a solution (coarse solution):

a)在会话关闭之前,强制hibernate拉取惰性集合

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

....

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

Question:

什么是最佳实践/常用方式/更优雅的方式来做到这一点?意味着将我的对象转换为JSON .

8 回答

  • 3

    使用Hibernate 4.1.6,引入了一个新功能来处理这些惰性关联问题 . 在hibernate.properties或hibernate.cfg.xml中启用hibernate.enable_lazy_load_no_trans属性时,将不再有LazyInitializationException .

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

  • 0

    必须获取多个集合时,您需要:

    因此,在您的情况下,您需要像这样的第一个JPQL查询:

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

    这样,您就可以通过2个SQL查询实现目标,并避免使用笛卡尔积 .

  • 3

    放置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();
    
  • 0

    它可能不是接近最佳实践的任何地方,但我通常在集合上调用 SIZE 以在同一事务中加载子项,就像您建议的那样 . 它很干净,不受子元素结构的任何变化的影响,并且产生的SQL开销很低 .

  • 2

    尝试使用 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);
    
  • 87

    您可以在同一事务中遍历Hibernate对象的Getters,以确保使用以下通用帮助程序类急切地获取所有延迟子对象:

    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

    不是最好的解决方案,但这是我得到的:

    1)使用此批注对要初始化的getter进行批注:

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

    2)在从数据库中读取对象后,使用此方法(可以放在泛型类中,或者可以使用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();
        }
    }
    
  • 4

    @Transactional 中使用 Hibernate.initialize() 来初始化延迟对象 .

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

    现在,在事务的一侧,您可以获得惰性对象 .

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

相关问题