初始化惰性集合

Initialize lazy collections

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

用例

我有一个 team 模型,其中有一个名为 employees 的关系被急切加载(我认为很明显这是一个集合)。反过来,employee 模型有一个惰性关系 registry,我只需要一些特定的操作,所以我根本不需要急切地加载它.

现在。我调用我的 teamService(通过 Spring 注入到我的 Struts2 控制器)以检索特定的 teamItem,该 teamItem 已经拥有他的集合 employees 加载。现在是时候为每个员工加载其注册表关系了。

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

Hibernate.initialize(employee.getRegistry());

当我执行此操作时,抛出了 org.hibernate.LazyInitializationException: could not initialize proxy - no Session 异常。

堆栈跟踪:

at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:167) ~[hibernate-core-3.6.10.Final.jar:3.6.10.Final]
at org.hibernate.Hibernate.initialize(Hibernate.java:414) ~[hibernate-core-3.6.10.Final.jar:3.6.10.Final]
at web.bkgd.simba.dao.registry.RegistryEmployeeDAO.initializeCollections(RegistryEmployeeDAO.java:438) ~[RegistryEmployeeDAO.class:?]
at web.bkgd.simba.service.abstractions.BaseService.initializeCollections(BaseService.java:142) ~[BaseService.class:?]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.7.0_79]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) ~[?:1.7.0_79]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.7.0_79]
at java.lang.reflect.Method.invoke(Method.java:606) ~[?:1.7.0_79]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317) ~[spring-aop-4.1.0.RELEASE.jar:4.1.0.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) ~[spring-aop-4.1.0.RELEASE.jar:4.1.0.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) ~[spring-aop-4.1.0.RELEASE.jar:4.1.0.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.proceedWithInvocation(TransactionInterceptor.java:98) ~[spring-tx-4.1.0.RELEASE.jar:4.1.0.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:266) ~[spring-tx-4.1.0.RELEASE.jar:4.1.0.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95) ~[spring-tx-4.1.0.RELEASE.jar:4.1.0.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.1.0.RELEASE.jar:4.1.0.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) ~[spring-aop-4.1.0.RELEASE.jar:4.1.0.RELEASE]
at com.sun.proxy.$Proxy1302.initializeCollections(Unknown Source) ~[?:?]
at web.bkgd.simba.controller.ajax.agenda.TeamController.doSomething(TeamController.java:177) ~[AjaxTeamController.class:?]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.7.0_79]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) ~[?:1.7.0_79]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.7.0_79]
at java.lang.reflect.Method.invoke(Method.java:606) ~[?:1.7.0_79]
at ognl.OgnlRuntime.invokeMethod(OgnlRuntime.java:870) ~[ognl-3.0.6.jar:?]
at ognl.OgnlRuntime.callAppropriateMethod(OgnlRuntime.java:1293) ~[ognl-3.0.6.jar:?]
at ognl.ObjectMethodAccessor.callMethod(ObjectMethodAccessor.java:68) ~[ognl-3.0.6.jar:?]
at com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor.callMethodWithDebugInfo(XWorkMethodAccessor.java:117) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor.callMethod(XWorkMethodAccessor.java:108) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at ognl.OgnlRuntime.callMethod(OgnlRuntime.java:1369) ~[ognl-3.0.6.jar:?]
at ognl.ASTMethod.getValueBody(ASTMethod.java:90) ~[ognl-3.0.6.jar:?]
at ognl.SimpleNode.evaluateGetValueBody(SimpleNode.java:212) ~[ognl-3.0.6.jar:?]
at ognl.SimpleNode.getValue(SimpleNode.java:258) ~[ognl-3.0.6.jar:?]
at ognl.Ognl.getValue(Ognl.java:494) ~[ognl-3.0.6.jar:?]
at ognl.Ognl.getValue(Ognl.java:458) ~[ognl-3.0.6.jar:?]
at com.opensymphony.xwork2.ognl.OgnlUtil.execute(OgnlUtil.java:309) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.ognl.OgnlUtil.compileAndExecute(OgnlUtil.java:340) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.ognl.OgnlUtil.getValue(OgnlUtil.java:307) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.DefaultActionInvocation.invokeAction(DefaultActionInvocation.java:423) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.DefaultActionInvocation.invokeActionOnly(DefaultActionInvocation.java:287) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:250) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at web.bkgd.simba.utility.interceptors.SessionCleanInterceptor.intercept(SessionCleanInterceptor.java:86) ~[SessionCleanInterceptor.class:?]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at org.apache.struts2.interceptor.DeprecationInterceptor.intercept(DeprecationInterceptor.java:41) ~[struts2-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at org.apache.struts2.interceptor.debugging.DebuggingInterceptor.intercept(DebuggingInterceptor.java:256) ~[struts2-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor.doIntercept(DefaultWorkflowInterceptor.java:167) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.validator.ValidationInterceptor.doIntercept(ValidationInterceptor.java:265) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor.doIntercept(AnnotationValidationInterceptor.java:76) ~[struts2-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.interceptor.ConversionErrorInterceptor.intercept(ConversionErrorInterceptor.java:138) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:229) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:229) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.interceptor.StaticParametersInterceptor.intercept(StaticParametersInterceptor.java:191) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at org.apache.struts2.interceptor.MultiselectInterceptor.intercept(MultiselectInterceptor.java:73) ~[struts2-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at org.apache.struts2.interceptor.DateTextFieldInterceptor.intercept(DateTextFieldInterceptor.java:125) ~[struts2-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at org.apache.struts2.interceptor.CheckboxInterceptor.intercept(CheckboxInterceptor.java:91) ~[struts2-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at org.apache.struts2.interceptor.FileUploadInterceptor.intercept(FileUploadInterceptor.java:253) ~[struts2-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor.intercept(ModelDrivenInterceptor.java:100) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor.intercept(ScopedModelDrivenInterceptor.java:141) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.interceptor.ChainingInterceptor.intercept(ChainingInterceptor.java:145) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.interceptor.PrepareInterceptor.doIntercept(PrepareInterceptor.java:171) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:98) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.interceptor.I18nInterceptor.intercept(I18nInterceptor.java:139) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at org.apache.struts2.interceptor.ServletConfigInterceptor.intercept(ServletConfigInterceptor.java:164) ~[struts2-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.interceptor.AliasInterceptor.intercept(AliasInterceptor.java:193) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) ~[xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor.intercept(ExceptionMappingInterceptor.java:189) [xwork-core-2.3.24.1.jar:2.3.24.1]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) [xwork-core-2.3.24.1.jar:2.3.24.1]
at web.bkgd.simba.utility.interceptors.LoginInterceptor.intercept(LoginInterceptor.java:113) [LoginInterceptor.class:?]
at com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:244) [xwork-core-2.3.24.1.jar:2.3.24.1]
at org.apache.struts2.impl.StrutsActionProxy.execute(StrutsActionProxy.java:54) [struts2-core-2.3.24.1.jar:2.3.24.1]
at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:564) [struts2-core-2.3.24.1.jar:2.3.24.1]
at org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:81) [struts2-core-2.3.24.1.jar:2.3.24.1]
at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:99) [struts2-core-2.3.24.1.jar:2.3.24.1]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) [catalina.jar:7.0.61]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) [catalina.jar:7.0.61]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220) [catalina.jar:7.0.61]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122) [catalina.jar:7.0.61]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505) [catalina.jar:7.0.61]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170) [catalina.jar:7.0.61]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103) [catalina.jar:7.0.61]
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950) [catalina.jar:7.0.61]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116) [catalina.jar:7.0.61]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:423) [catalina.jar:7.0.61]
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1079) [tomcat-coyote.jar:7.0.61]
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:620) [tomcat-coyote.jar:7.0.61]
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:318) [tomcat-coyote.jar:7.0.61]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [?:1.7.0_79]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [?:1.7.0_79]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-coyote.jar:7.0.61]
at java.lang.Thread.run(Thread.java:745) [?:1.7.0_79]

编辑 01-12-2015

我已尝试按照 Predrag Maric 的回答进行操作,但由于似乎仍然无法正常工作,我需要进行一些修改。

所以:

  1. Implement Open Session in View pattern

没办法。此解决方案可能会导致非常深的关系加载,特别是对于 JSON 序列化。当然我总是可以定义 <param name="includeProperties"> 标签并加载我需要的东西,但我还是有点害怕表演。

  1. Initialize whatever you need in the first service

这就是重点。为了实现这个功能,我改变了我的动作和 DAO 结构。我使用了一些反射来创建一个通用方法,我可以在我的应用程序的每个 DAO 中使用它。

它的工作原理非常简单:

例子

输入:

第一个字符串的方法简单地调用 Hibernate.initialize() 方法,第二个字符串拆分字符串以加载:

这里有代码:

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

但是我在日志中看到的是只加载了第一个关系

[DEBUG] 01-12-2015 14:06:52 Controller - relation: Employee{data1="data1", data2="data2", data3="data3", data4="data4", data5="data5"}
[DEBUG] 01-12-2015 14:06:52 Controller - child: [!!!model.Registry_$$_javassist_17@75ce9b7d=>org.hibernate.LazyInitializationException:could not initialize proxy - no Session!!!]

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

  1. 从控制器调用teamService(一个事务)
  2. 使用第一次调用的结果调用 employeeService(另一个事务)

你的实体在第一次调用后与会话分离(如果我错了请纠正我,我假设你已经在服务层配置了事务),所以这失败了。你应该

  1. 实施Open Session in View模式
  2. 在第一个服务中初始化你需要的任何东西

至于2.如果这个服务用在其他不需要employee.registry初始化的地方,可以用一个参数来控制初始化

teamService.getTeam(id, init);
// and then in getTeam()
if (init) {
    Hibernate.initialize(employee.getRegistry());
}

为了使其更通用,您可以创建一个 InitConfig class,它将传递给需要这种行为的每个服务。您只需根据需要在其中添加特定参数即可。

public class InitConfig {
    ...
    private boolean initRegistry;
    ...
}

teamService.getTeam(id, initConfig);
// and then in getTeam()
if (initConfig.getInitRegistry()) {
    Hibernate.initialize(employee.getRegistry());
}

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

尝试将参数 "hibernate.enable_lazy_load_no_trans" 设置为您的休眠配置。

sfBuilder.getProperties().put("hibernate.enable_lazy_load_no_trans",
            "true");

这个参数解决了我的问题。希望对你有帮助。

已编辑: 谨慎使用。查看 this post