java spring,hibernate:无法懒惰地初始化集合

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10947642/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 03:11:53  来源:igfitidea点击:

spring, hibernate: failed to lazily initialize a collection

javaspringhibernatecollectionslazy-initialization

提问by spinner0815

I have a problem with lazy initialisation. I can't find a solution.

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

Exception:

例外:

[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 method:

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

Server.java

服务器端.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

应用程序.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
}

I don't really know why I cant read the list of applications from the server

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

If anybody could help it would be great.

如果有人可以提供帮助,那就太好了。

回答by Matt

Most likely because you're setting applications collection to lazy load. So when you return from the initial call to serviceFacade.getServers(), my guess is that you no longer have the session opened that was used to fetch the list of servers.

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

As a result, when you iterator through the applications, the session was closed, so it can't load the actual contents of the collection.

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

You have three possibilities:

你有三种可能:

  1. Keep the session open (google for binding hibernate session to thread, or if the readStatusCache method is in a spring managed object, just add an @Transactional annotation to it, or the entry point from which it is called).
  2. Change the applications collection to be an eager load
  3. have your dao layer fully initialize the applications collection (see Hibernate.initialize method) while the session is still open.
  1. 保持会话打开(谷歌将休眠会话绑定到线程,或者如果 readStatusCache 方法在 spring 管理的对象中,只需向它添加一个 @Transactional 注释,或调用它的入口点)。
  2. 将应用程序集合更改为急切加载
  3. 让您的 dao 层在会话仍然打开时完全初始化应用程序集合(请参阅 Hibernate.initialize 方法)。

回答by Thihara

The two loops

两个循环

   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
    }

are being called by two separate threads simultaneously. You can check if this is the case by synchronizing the readStatusCache()method. But this should be done in the Spring layer. Are you using transactions etc. properly?

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

Please read this answer for more on thread safety with spring and hibernate. Spring-Hibernate used in a webapp,what are strategies for Thread safe session management

请阅读此答案以了解有关 spring 和 hibernate 线程安全的更多信息。webapp中使用的Spring-Hibernate,线程安全会话管理的策略是什么

回答by Thihara

When I got the same exception, the solution was to add the @Transactional annotation to the method in the controller.

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