【问题标题】:spring, hibernate: failed to lazily initialize a collection春天,休眠:未能懒惰地初始化一个集合
【发布时间】:2012-06-08 11:06:16
【问题描述】:

我遇到了延迟初始化的问题。我找不到解决办法。

例外:

[pool-1-thread-12] ERROR:12:20:14.840 o.h.LazyInitializationException - failed to lazily initialize a collection of role: de.beeld.forges.domain.Server.applications, no session or session was closed
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: de.beeld.forges.domain.Server.applications, no session or session was closed
    at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:380)
    at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:372)
[pool-2-thread-1] ERROR:12:20:14.840 o.s.s.support.MethodInvokingRunnable - Invocation of method 'readStatusCache' on target class [class de.beeld.forges.task.annotation.ScheduledProcessor$$EnhancerByCGLIB$$ee649dc3] failed
java.util.ConcurrentModificationException: null
    at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
    at java.util.AbstractList$Itr.next(AbstractList.java:343)

hibernate.xml

<!-- Hibernate SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
    p:dataSource-ref="standardDataSource" p:lobHandler-ref="defaultLobHandler">
    <property name="annotatedClasses">
        <list>
            <value>de.beeld.forges.domain.Server</value>
            <value>de.beeld.forges.domain.Application</value>
            <value>de.beeld.forges.domain.Forge</value>
        </list>
    </property>
</property>
</bean>
<bean id="defaultLobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler" />
<!-- Read in DAOs from the hibernate package -->
<context:component-scan base-package="de.beeld.forges.dao" />
<bean id="transactionManager"
    class="org.springframework.orm.hibernate3.HibernateTransactionManager"
    p:sessionFactory-ref="sessionFactory" />

<bean id="transactionTemplate"
    class="org.springframework.transaction.support.TransactionTemplate">
    <property name="transactionManager" ref="transactionManager" />
</bean>

<tx:annotation-driven transaction-manager="transactionManager" />
    <context:component-scan base-package="de.beeld">
    <context:exclude-filter expression="org.springframework.stereotype.Controller"
        type="annotation" />
    <context:exclude-filter expression="org.springframework.stereotype.Repository"
        type="annotation" />
</context:component-scan>
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />

readStatusCache 方法:

public void readStatusCache() {
    String execCommand = "java -jar ...";
    List<Future<Map<Long, Integer>>> list = new ArrayList<Future<Map<Long, Integer>>>();
    String serverName = null;
    for (Server server : serviceFacade.getServers()) {
        serverName = server.getName();

        Callable<Map<Long, Integer>> worker = new ApplicationStatusReader2(server.getApplications(),
                sshConnector, execCommand, serverName);
        Future<Map<Long, Integer>> submit = this.serviceFacade.getExecutor().submit(worker);
        list.add(submit);
    }

    for (Future<Map<Long, Integer>> future : list) {
        //do stuff
    }
}

服务器.java

@Entity
@org.hibernate.annotations.Entity(dynamicUpdate = true)
public class Server implements DomainObject, Comparable<Server> {
private static final long serialVersionUID = -8920952435734596243L;

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@Column(unique = true, nullable = false)
@NotEmpty
private String name;

@Column(nullable = false)
@NotEmpty
@Pattern(regexp = "^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])$", message = "The ip must be in format xxx.xxx.xxx.xxx")
private String ip;

@Column(nullable = false)
@NotEmpty
private String fqdn;

@OneToMany(mappedBy = "server", fetch = FetchType.LAZY)
private List<Application> applications;

@Version
private int version;

//getter and setter
}

Application.java

@Entity
public class Application implements DomainObject {
private static final long serialVersionUID = -8127137156319959239L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
private Server server;
@Column(nullable = false)
@NotEmpty
private String name;
@Column(nullable = false)
@NotEmpty
private String location;
@Column(nullable = false)
@NotEmpty
private String binDir;
private String confDir;
private boolean isContainer = false;
private String containerDir;
private String startup = "startup.sh";
private String shutdown = "shutdown.sh";
@ManyToOne(fetch = FetchType.LAZY)
@Fetch(FetchMode.JOIN)
private Forge forge;
@ManyToOne(fetch = FetchType.LAZY)
@Fetch(FetchMode.JOIN)
private Application parent;
@OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
private List<Application> offsprings;
@NotEmpty
private String blueprint;
private Integer replaceable = 0;
private Integer running = 0;
@Version
private int version;

//getter and setter
}

我真的不知道为什么我无法从服务器读取应用程序列表

如果有人能帮忙就太好了。

【问题讨论】:

    标签: java spring hibernate collections lazy-initialization


    【解决方案1】:

    很可能是因为您将应用程序集合设置为延迟加载。因此,当您从对 serviceFacade.getServers() 的初始调用返回时,我的猜测是您不再打开用于获取服务器列表的会话。

    因此,当您遍历应用程序时,会话已关闭,因此无法加载集合的实际内容。

    你有三种可能:

    1. 保持会话打开(google 用于将休眠会话绑定到线程,或者如果 readStatusCache 方法在 spring 托管对象中,只需向其添加 @Transactional 注释,或调用它的入口点)。李>
    2. 将应用程序集合更改为即时加载
    3. 让您的 dao 层在会话仍处于打开状态时完全初始化应用程序集合(请参阅 Hibernate.initialize 方法)。

    【讨论】:

    • 感谢您的回答。问题是它在一个服务器集群上工作,而在另一个服务器集群上却没有。目前有很好的建议,我稍后会尝试
    • 感谢您提供如此清晰的解决方案...这对我来说是一场噩梦(在互联网上搜索了 2 小时)。对我来说主要问题是我不能做 1(方法在弹簧对象之外)加上我正在开发桌面应用程序。我选择了 Hibernate.initialize(myModel.getRelatedCollection());效果很好
    【解决方案2】:

    两个循环

       for (Server server : serviceFacade.getServers()) {
            serverName = server.getName();
    
            Callable<Map<Long, Integer>> worker = new ApplicationStatusReader2(server.getApplications(),
                    sshConnector, execCommand, serverName);
            Future<Map<Long, Integer>> submit = this.serviceFacade.getExecutor().submit(worker);
            list.add(submit);
        }
    
        for (Future<Map<Long, Integer>> future : list) {
            //do stuff
        }
    

    同时被两个单独的线程调用。您可以通过同步readStatusCache() 方法来检查是否是这种情况。但这应该在 Spring 层中完成。您是否正确使用交易等?

    请阅读此答案以了解有关 spring 和 hibernate 的线程安全的更多信息。 Spring-Hibernate used in a webapp,what are strategies for Thread safe session management

    【讨论】:

    • 我不知道我是否正确使用交易等。我是使用spring和hibernate的新手。其中 readStatusCache() 方法的 ScheduledProcessor 类被注释为 Transactional 并且 serviceFacade 也是 Transactional。我也尝试同步 readStatusCache() 但异常仍然存在。该项目还使用GenericDaoImpl extends HibernateDaoSupport。我希望你明白我的意思。
    • 好像没问题。我能想到抛出你的堆栈跟踪的唯一原因是这个。所以我完全失去了弹药伙伴。祝你好运......
    【解决方案3】:

    当我遇到同样的异常时,解决方法是在控制器中的方法中添加@Transactional注解。

    【讨论】:

      猜你喜欢
      • 2019-01-24
      • 2011-07-21
      • 2014-09-25
      • 1970-01-01
      • 1970-01-01
      • 2018-12-12
      相关资源
      最近更新 更多