Весна, спящий режим: не удалось лениво инициализировать коллекцию

У меня проблема с ленивой инициализацией. Я не могу найти решение.

Исключение:

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

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
}

Я действительно не знаю, почему я не могу прочитать список приложений с сервера

Если бы кто-нибудь мог помочь, это было бы здорово.

3 ответа

Решение

Скорее всего, потому что вы устанавливаете коллекцию приложений на ленивую загрузку. Поэтому, когда вы возвращаетесь из первоначального вызова serviceFacade.getServers(), я предполагаю, что у вас больше нет открытого сеанса, который использовался для получения списка серверов.

В результате, когда вы выполняли итерацию по приложениям, сеанс был закрыт, поэтому он не может загрузить фактическое содержимое коллекции.

У вас есть три возможности:

  1. Оставьте сеанс открытым (Google для привязки сеанса гибернации к потоку или, если метод readStatusCache находится в управляемом объекте Spring, просто добавьте к нему аннотацию @Transactional или точку входа, из которой он вызывается).
  2. Измените коллекцию приложений, чтобы быть загруженной
  3. пусть ваш слой dao полностью инициализирует коллекцию приложений (см. метод Hibernate.initialize), пока сеанс еще открыт.

Когда я получил то же исключение, решением было добавить аннотацию @Transactional к методу в контроллере.

Две петли

   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 используется в веб-приложении, каковы стратегии для безопасного управления потоками потока

Другие вопросы по тегам