Question

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:

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

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

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

If anybody could help it would be great.

Was it helpful?

Solution

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.

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.

OTHER TIPS

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?

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

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top