Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/390.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/hibernate/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 初始化惰性集合_Java_Hibernate_Lazy Loading - Fatal编程技术网

Java 初始化惰性集合

Java 初始化惰性集合,java,hibernate,lazy-loading,Java,Hibernate,Lazy Loading,我正在开发一个Struts2+Spring+HibernateWebApp,在检索一个对象或该对象的集合后,我需要初始化一个惰性集合 用例 我有一个团队模型,该模型中有一个名为员工的关系(我认为这显然是一个集合)。反过来,employee模型有一个惰性关系registry,我只需要它来执行一些特定的操作,所以我根本不需要急切地加载它 现在。我调用我的teamService(使用Spring注入我的Struts2控制器),以检索已加载其收藏员工的特定teamItem。现在是为每个员工加载其注册表关

我正在开发一个Struts2+Spring+HibernateWebApp,在检索一个对象或该对象的集合后,我需要初始化一个惰性集合

用例

我有一个团队模型,该模型中有一个名为员工的关系(我认为这显然是一个集合)。反过来,employee模型有一个惰性关系registry,我只需要它来执行一些特定的操作,所以我根本不需要急切地加载它

现在。我调用我的
teamService
(使用Spring注入我的Struts2控制器),以检索已加载其收藏员工的特定
teamItem
。现在是为每个员工加载其注册表关系的时候了

使用
employeeService
(也将其注入到我的Struts2控制器中,使用Spring)调用他的DAO的
initializeCollections()
方法,该方法应加载惰性集合调用

Hibernate.initialize(employee.getRegistry());
执行此操作时,会出现
org.hibernate.LazyInitializationException:无法初始化代理-不会引发会话
异常

堆栈跟踪:

  • 我做错什么了吗
  • 有没有其他方法来加载这个惰性集合
编辑01-12-2015

我试着遵循Predrag Maric的回答,我还有一些编辑要做,因为似乎仍然不起作用

因此:

  • 在视图模式中实现开放会话
  • 不可能。此解决方案可能会导致关系的深度加载,特别是JSON序列化。当然,我总是可以定义
    标记并加载所需的内容,但我仍然有点害怕性能

  • 初始化第一次服务中需要的任何内容
  • 这就是重点。为了实现这个功能,我改变了我的操作和DAO结构。我使用了一些反射来创建一个通用方法,我可以在应用程序的每个DAO中使用它

    它的工作原理非常简单:

    示例

    输入:

    • beanClass:团队
    • 模型:未加载关系的团队
    • 关系:列表:{“关系”,“关系.子关系.孙子关系”}
    第一个字符串的方法只调用
    Hibernate.initialize()
    方法,第二个字符串的方法拆分字符串以加载:

    • realization,返回
      初始化设置(relation.class,relation,“childRelation.grandrelation”)
    • childRelation,返回
      初始化设置(childRelation.class,childRelation,“孙子关系”)
    • 孙辈关系
    这里有代码:

    团队DAO

    这些方法是在从数据库加载团队bean或团队bean列表后调用的

    /**
     * Initialize relations. This method returns true if a correct
     * initialization of the relation has been made by hibernate, false in any
     * other case. If the relations object is a List, the method recursively
     * call himself in order to initialize every single instance of the list.
     * If the relations string contains a "." it means that a grandchild
     * relation has to be loaded.
     *
     * @param beanClass
     * @param model
     * @param relations
     * @return boolean
     */
    public boolean initializeRelations(Class beanClass, BaseModel model, Object relations) {
        // Check if relations is a List
        if (relations instanceof List) {
            // Recursively call initializeRelations
            for (String relation : (List<String>) relations) {
                return initializeRelations(beanClass, model, relation);
            }
        } else if (relations instanceof String) {
            // If relations contains "." then a grandChild relation has to be loaded.
            if (((String) relations).contains(".")) {
                String[] childRelations = ((String) relations).split("\\.");
    
                // Initialize the child relation
                Object newChildRelations = initializeRelation(beanClass, model, childRelations[0]);
    
                if (newChildRelations == null) {
                    return false;
                } else if (newChildRelations instanceof BaseModel) {
                    initializeRelations(newChildRelations.getClass(), model, (Object[]) Arrays.copyOfRange(childRelations, 1, childRelations.length));
                } else if (newChildRelations instanceof List) {
                    for (Object newChildRelation : childRelations) {
                        initializeRelations(newChildRelation.getClass(), model, (Object[]) Arrays.copyOfRange(childRelations, 1, childRelations.length));
                    }
                }
            } else {
                Object newChildRelations = initializeRelation(beanClass, model, (String) relations);
    
                return newChildRelations == null;
            }
        }
    
        return false;
    }
    
    private Object initializeRelation(Class beanClass, BaseModel model, String relation) {
        try {
            for (PropertyDescriptor pd : Introspector.getBeanInfo(beanClass).getPropertyDescriptors()) {
                if (pd.getReadMethod() != null && !"class".equals(pd.getName()) && relation.toLowerCase().equals(pd.getName().toLowerCase()) && pd.getReadMethod().invoke(model) == null) {
                    Hibernate.initialize(pd.getReadMethod().invoke(model));
    
                    return pd.getReadMethod().invoke(model);
                }
            }
        } catch (IntrospectionException ex) {
            LOG.warn("Cannot initialize ralation", ex);
        } catch (HibernateException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
            LOG.warn("Cannot initialize ralation", ex);
        }
        return null;
    }
    

    是的,您可以使用
    Hibernate.initialize()
    初始化惰性关系,但这需要一个活动的Hibernate会话。你是

  • 从控制器调用
    teamService
    (一个事务)
  • 调用
    employeeService
    ,并显示第一次调用的结果(另一个事务)
  • 在第一次调用之后,您的实体将从会话中分离(如果我错了,请纠正我,我假设您已经在服务层上配置了事务),因此这将失败。你也应该

  • 实现模式
  • 初始化第一次服务中需要的任何内容
  • 对于2,如果此服务用于其他不需要初始化
    employee.registry
    的地方,则可以使用参数来控制初始化

    teamService.getTeam(id, init);
    // and then in getTeam()
    if (init) {
        Hibernate.initialize(employee.getRegistry());
    }
    
    为了使其更通用,您可以创建一个
    InitConfig
    类,该类将传递给需要这种行为的每个服务。您只需根据需要在其中添加特定参数

    public class InitConfig {
        ...
        private boolean initRegistry;
        ...
    }
    
    teamService.getTeam(id, initConfig);
    // and then in getTeam()
    if (initConfig.getInitRegistry()) {
        Hibernate.initialize(employee.getRegistry());
    }
    

    几个月前我遇到了一些问题。 我已经解决了这个问题,在我的会话工厂生成器中设置了一个参数

    尝试将参数“hibernate.enable\u lazy\u load\u no\u trans”设置到hibernate配置中

    sfBuilder.getProperties().put("hibernate.enable_lazy_load_no_trans",
                "true");
    
    这个参数解决了我的问题。希望能有帮助

    编辑:
    小心使用。签出。

    我仍然收到一个
    org.hibernate.LazyInitializationException:无法初始化代理-没有会话错误。你能看一看我的编辑吗?“但我还是有点害怕表演。”是的,但在你尝试和分析之前……很多关于OSIV(至少是Spring实现)的批评实际上是不正确的。这是最简单、最透明的解决方案,不需要全部或什么都不做:按照“做最简单的事情”的原则,在OSIV开销太大的情况下,尝试并进一步优化它。只需注意:在
    初始化
    方法中,当
    关系
    无效时,您不会处理这种情况一个数组,但您在那里传递了一个数组:
    (Object[])array.copyOfRange(…)
    它永远不会是数组。或者更好。。。“parent”方法调用
    initializeRatings()
    ,总是创建一个列表。只有递归调用
    initializeRelations()
    时,才会使用
    relations instanceOf String
    语句。但是谢谢你的来信。你的回答解决了我的问题。网络上对此参数的看法各不相同,当然,N+1问题总是指日可待,但atm似乎没有很多问题。谢谢你@RodrigoAlmeida
    public class InitConfig {
        ...
        private boolean initRegistry;
        ...
    }
    
    teamService.getTeam(id, initConfig);
    // and then in getTeam()
    if (initConfig.getInitRegistry()) {
        Hibernate.initialize(employee.getRegistry());
    }
    
    sfBuilder.getProperties().put("hibernate.enable_lazy_load_no_trans",
                "true");