wake-up-neo.com

Ruhezustand/Frühling: Initialisierung fehlgeschlagen - keine Sitzung oder Sitzung wurde geschlossen

Für eine Antwort scrollen Sie bis zum Ende ...

Das Grundproblem ist dasselbe wie mehrfach gefragt. Ich habe ein einfaches Programm mit zwei POJOs Event und User - wobei ein User mehrere Events haben kann.

@Entity
@Table
public class Event {
 private Long id;
 private String name;
 private User user;

 @Column
 @Id
 @GeneratedValue
 public Long getId() {return id;}
 public void setId(Long id) { this.id = id; }

 @Column
 public String getName() {return name;}
 public void setName(String name) {this.name = name;}

 @ManyToOne
 @JoinColumn(name="user_id")
 public User getUser() {return user;}
 public void setUser(User user) {this.user = user;}

}

Der Benutzer:

@Entity
@Table
public class User {
 private Long id;
 private String name;
 private List<Event> events;

 @Column
 @Id
 @GeneratedValue
 public Long getId() { return id; }
 public void setId(Long id) { this.id = id; }

 @Column
 public String getName() { return name; }
 public void setName(String name) { this.name = name; }

 @OneToMany(mappedBy="user", fetch=FetchType.LAZY)
 public List<Event> getEvents() { return events; }
 public void setEvents(List<Event> events) { this.events = events; }

}

Hinweis: Dies ist ein Beispielprojekt. Ich wirklich möchte Lazy Fetching hier verwenden.

Jetzt müssen wir spring und hibernate konfigurieren und eine einfache basic-db.xml zum Laden haben:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/aop 
           http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">


 <bean id="myDataSource" class="org.Apache.commons.dbcp.BasicDataSource"
  destroy-method="close"  scope="thread">
  <property name="driverClassName" value="com.mysql.jdbc.Driver" />
  <property name="url" value="jdbc:mysql://192.168.1.34:3306/hibernateTest" />
  <property name="username" value="root" />
  <property name="password" value="" />
  <aop:scoped-proxy/>
 </bean>

 <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
  <property name="scopes">
   <map>
    <entry key="thread">
     <bean class="org.springframework.context.support.SimpleThreadScope" />
    </entry>
   </map>
  </property>
 </bean>

 <bean id="mySessionFactory"
  class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean" scope="thread">
  <property name="dataSource" ref="myDataSource" />
  <property name="annotatedClasses">
   <list>
    <value>data.model.User</value>
    <value>data.model.Event</value>
   </list>
  </property>
  <property name="hibernateProperties">
   <props>
    <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
    <prop key="hibernate.show_sql">true</prop>
    <prop key="hibernate.hbm2ddl.auto">create</prop>
   </props>
  </property>
  <aop:scoped-proxy/>

 </bean>

 <bean id="myUserDAO" class="data.dao.impl.UserDaoImpl">
  <property name="sessionFactory" ref="mySessionFactory" />
 </bean>

 <bean id="myEventDAO" class="data.dao.impl.EventDaoImpl">
  <property name="sessionFactory" ref="mySessionFactory" />
 </bean>

</beans>

Hinweis: Ich habe mit dem CustomScopeConfigurer und SimpleThreadScope herumgespielt, aber das hat nichts geändert.

Ich habe ein einfaches Dao-Impl (nur das Einfügen des userDao - das EventDao ist ziemlich gleich - außer ohne die Funktion "listWith":


public class UserDaoImpl implements UserDao{

 private HibernateTemplate hibernateTemplate;

 public void  setSessionFactory(SessionFactory sessionFactory) {
  this.hibernateTemplate = new HibernateTemplate(sessionFactory);

 }

 @SuppressWarnings("unchecked")
 @Override
 public List listUser() {
  return hibernateTemplate.find("from User");
 }

 @Override
 public void saveUser(User user) {
  hibernateTemplate.saveOrUpdate(user);

 }

 @Override
 public List listUserWithEvent() {

  List users = hibernateTemplate.find("from User");
  for (User user : users) {
   System.out.println("LIST : " + user.getName() + ":");
   user.getEvents().size();
  }
  return users;
 }

}

Ich erhalte die org.hibernate.LazyInitializationException - Initialisierung einer Rollensammlung fehlgeschlagen: data.model.User.events, keine Sitzung oder Sitzung wurde in der Zeile mit user.getEvents () geschlossen. Size () ;

Und zu guter Letzt hier ist die Testklasse, die ich benutze:


public class HibernateTest {

 public static void main(String[] args) {

  ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("basic-db.xml");


  UserDao udao = (UserDao) ac.getBean("myUserDAO");
  EventDao edao = (EventDao) ac.getBean("myEventDAO");


  System.out.println("New user...");
  User user = new User();
  user.setName("test");

  Event event1 = new Event();
  event1.setName("Birthday1");
  event1.setUser(user);

  Event event2 = new Event();
  event2.setName("Birthday2");
  event2.setUser(user);

  udao.saveUser(user);
  edao.saveEvent(event1);
  edao.saveEvent(event2);

  List users = udao.listUserWithEvent();
  System.out.println("Events for users");
  for (User u : users) {

   System.out.println(u.getId() + ":" + u.getName() + " --");
   for (Event e : u.getEvents())
   {
    System.out.println("\t" + e.getId() + ":" + e.getName());
   }
  }

  ((ConfigurableApplicationContext)ac).close();
 }

}

und hier ist die Ausnahme:

 1621 [main] ERROR org.hibernate.LazyInitializationException - Initialisierung einer Rollensammlung fehlgeschlagen: data.model.User.events, keine Sitzung oder Sitzung wurde geschlossen 
 Org.hibernate.LazyInitializationException: Fehler beim verzögerten Initialisieren einer Auflistung der Rolle: data.model.User.events, keine Sitzung oder Sitzung wurde geschlossen 
 um org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException (AbstractPersistentCollection.Java:380) 
 um org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected (AbstractPersistentCollection.Java:372) 
 um org.hibernate.collection.AbstractPersistentCollection.readSize (AbstractPersistentCollection.Java:119) [ .PersistentBag.size (PersistentBag.Java:248) 
 At data.dao.impl.UserDaoImpl.listUserWithEvent (UserDaoImpl.Java:38) 
 At HibernateTest.main (HibernateTest.Java:44) 
 Ausnahme im Thread "main" org.hibernate.LazyIniti alizationException: Fehler beim verzögerten Initialisieren einer Auflistung von Rollen: data.model.User.events, keine Sitzung oder Sitzung wurde geschlossen 
 unter org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException (AbstractPersistentCollection.Java:380) 
 um org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected (AbstractPersistentCollection.Java:372) 
 um org.hibernate.collection.AbstractPersistentCollection.readSize (AbstractPersistentCollection.Java:119) [ .collection.PersistentBag.size (PersistentBag.Java:248) 
 at data.dao.impl.UserDaoImpl.listUserWithEvent (UserDaoImpl.Java:38) 
 at HibernateTest.main (HibernateTest.Java:44 ) 

Dinge versucht, aber nicht funktioniert:

  • ein threadScope zuweisen und beanfactory verwenden (ich habe "request" oder "thread" verwendet - kein Unterschied bemerkt):
 // Umfang Zeug 
 Umfang threadScope = new SimpleThreadScope (); 
 ConfigurableListableBeanFactory beanFactory = ac.getBeanFactory (); 
 beanFactory.registerScope ("request", threadScope ); 
 ac.refresh (); 
 ... 
  • Einrichten einer Transaktion durch Abrufen des Sitzungsobjekts vom Deo:
 ... 
 Transaktion tx = ((UserDaoImpl) udao) .getSession (). BeginTransaction (); 
 Tx.begin (); 
 Benutzer = udao .listUserWithEvent (); 
 ... 
  • abrufen einer Transaktion innerhalb von listUserWithEvent ()
 öffentliche Liste listUserWithEvent () {
 SessionFactory sf = hibernateTemplate.getSessionFactory (); 
 Sitzung s = sf.openSession (); 
 Transaktion tx = s.beginTransaction (); 
 tx.begin (); 
 
 List users = hibernateTemplate.find ("from User"); 
 for (User user: users) { 
 System.out.println ("LIST:" + user.getName () + ":"); 
 User.getEvents (). Size (); 
} 
 tx.commit (); 
 Benutzer zurückgeben; 
} 

Ich habe jetzt wirklich keine Ideen mehr. Außerdem funktioniert die Verwendung von listUser oder listEvent einwandfrei.

Schritt vorwärts:

Dank Thierry bin ich einen Schritt weiter gekommen (glaube ich). Ich habe die MyTransaction-Klasse erstellt und mache dort meine ganze Arbeit, um alles aus dem Frühling zu holen. Das neue Haupt sieht so aus:


 public static void main(String[] args) {

  ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("basic-db.xml");

  // getting dao
  UserDao udao = (UserDao) ac.getBean("myUserDAO");
  EventDao edao = (EventDao) ac.getBean("myEventDAO");

  // gettting transaction template
  TransactionTemplate transactionTemplate = (TransactionTemplate) ac.getBean("transactionTemplate");

  MyTransaction mt = new MyTransaction(udao, edao);
  transactionTemplate.execute(mt);

  ((ConfigurableApplicationContext)ac).close();
 }

Leider gibt es jetzt eine Nullzeiger-Ausnahme @: user.getEvents (). Size (); (im daoImpl).

Ich weiß, dass es nicht null sein sollte (weder von der Ausgabe in der Konsole noch vom DB-Layout).

Hier ist die Konsolenausgabe für weitere Informationen (ich habe nach user.getEvent () == null gesucht und "EVENT is NULL" gedruckt):

 Neuer Benutzer ... 
 Ruhezustand: In Benutzerwerte (Name) einfügen (?) 
 Ruhezustand: In Benutzerwerte (Name) einfügen (?) 
 Ruhezustand : In Ereignis (Name, Benutzer_ID) Werte einfügen (?,?) 
 Ruhezustand: In Ereignis (Name, Benutzer_ID) Werte einfügen (?,?) 
 Ruhezustand: In Ereignis (Name, Benutzer_ID) einfügen ) Werte (?,?) 
 Benutzer auflisten: 
 Ruhezustand: Wählen Sie Benutzer0_.id als ID0_, Benutzer0_.name als Name0_ aus Benutzer0 _ 
 1: Benutzer1 
 2: Benutzer2 
 Ereignisse auflisten: 
 Ruhezustand: Wählen Sie event0_.id als id1_, event0_.name als name1_, event0_.user_id als user3_1_ aus Event event0 _ 
 1: Geburtstag1 für 1: Benutzer1 
 2: Geburtstag2 für 1: Benutzer1 
 3: Hochzeit für 2: Benutzer2 
 Ruhezustand: Wählen Sie Benutzer0_.id als id0_, Benutzer0_.name als name0_ aus Benutzer0 _ 
 Ereignisse für Benutzer 
 1: Benutzer1 - 
 EREIGNIS ist NULL 
 2: Benutzer2 - 
 EREIGNIS ist NULL 

Sie können das Beispielprojekt von http://www.gargan.org/code/hibernate-test1.tgz herunterladen (es ist ein Eclipse/Maven-Projekt).

Die Lösung (für Konsolenanwendungen)

Es gibt tatsächlich zwei Lösungen für dieses Problem - abhängig von Ihrer Umgebung:

Für eine Konsolenanwendung benötigen Sie eine Transaktionsvorlage, die die aktuelle DB-Logik erfasst und die Transaktion erledigt:


public class UserGetTransaction implements TransactionCallback{

 public List users;

 protected ApplicationContext context;

 public UserGetTransaction (ApplicationContext context) {
  this.context = context;
 }

 @Override
 public Boolean doInTransaction(TransactionStatus arg0) {
  UserDao udao = (UserDao) ac.getBean("myUserDAO");
  users = udao.listUserWithEvent();
  return null;
 }

}

Sie können dies nutzen, indem Sie anrufen:


 TransactionTemplate transactionTemplate = (TransactionTemplate) context.getBean("transactionTemplate");
 UserGetTransaction mt = new UserGetTransaction(context);
 transactionTemplate.execute(mt);

Damit dies funktioniert, müssen Sie die Vorlagenklasse für spring definieren (dh in Ihrer basic-db.xml):

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

Eine andere (mögliche) Lösung

danke andi

    PlatformTransactionManager transactionManager = (PlatformTransactionManager) applicationContext.getBean("transactionManager");
    DefaultTransactionAttribute transactionAttribute = new DefaultTransactionAttribute(TransactionDefinition.PROPAGATION_REQUIRED);

transactionAttribute.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
    TransactionStatus status = transactionManager.getTransaction(transactionAttribute);
    boolean success = false;
    try {
      new UserDataAccessCode().execute();
      success = true;
    } finally {
      if (success) {
        transactionManager.commit(status);
      } else {
        transactionManager.rollback(status);
      }
    }

Die Lösung (für Servlets)

Servlets sind kein so großes Problem. Wenn Sie ein Servlet haben, können Sie am Anfang Ihrer Funktion einfach eine Transaktion starten und binden und am Ende wieder binden:

public void doGet(...) {
  SessionFactory sessionFactory = (SessionFactory) context.getBean("sessionFactory");
  Session session = SessionFactoryUtils.getSession(sessionFactory, true);
  TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));

// Your code....

  TransactionSynchronizationManager.unbindResource(sessionFactory);
}
38
Niko

Ich denke, Sie sollten nicht die Transaktionsmethoden für Hibernate-Sitzungen verwenden, sondern lassen Sie den Frühling dies tun.

Fügen Sie dies zu Ihrem Frühjahrskonfekt hinzu:

<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="mySessionFactory" />
</bean>

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

und dann würde ich Ihre Testmethode ändern, um die Frühjahrstransaktionsvorlage zu verwenden:

public static void main(String[] args) {
    // init here (getting dao and transaction template)

    transactionTemplate.execute(new TransactionCallback() {
        @Override
        public Object doInTransaction(TransactionStatus status) {
          // do your hibernate stuff in here : call save, list method, etc
        }
    }
}

als Randbemerkung sind @OneToMany-Verknüpfungen standardmäßig faul, daher müssen Sie sie nicht faul kommentieren. (@ * ToMany sind standardmäßig LAZY, @ * ToOne sind standardmäßig EAGER)

BEARBEITEN: Hier ist was jetzt aus dem Winterschlaf-Standpunkt passiert:

  • offene Sitzung (mit Transaktionsstart)
  • einen Benutzer speichern und in der Sitzung aufbewahren (siehe Sitzungscache als Entitäts-Hashmap, wobei der Schlüssel die Entitäts-ID ist)
  • speichern Sie ein Ereignis und behalten Sie es in der Sitzung bei
  • speichern Sie ein anderes Ereignis und behalten Sie es in der Sitzung bei
  • ... dasselbe bei allen Speicheroperationen ...

  • laden Sie dann alle Benutzer (die Abfrage "von Benutzern")

  • an diesem Punkt sehen Sie den Winterschlaf, dass sich das Objekt bereits in seiner Sitzung befindet. Löschen Sie also das Objekt, das es von der Anforderung erhalten hat, und geben Sie das Objekt aus der Sitzung zurück.
  • bei Ihrem Benutzer in der Sitzung wurde die Ereignissammlung nicht initialisiert. Sie erhalten also null.
  • ...

Hier einige Punkte, um Ihren Code zu verbessern:

  • wenn in Ihrem Modell keine Sammlungsbestellung erforderlich ist, verwenden Sie "Set" und nicht "List" für Ihre Sammlungen (private Set-Ereignisse, nicht private List-Ereignisse).
  • geben Sie in Ihrem Modell Ihre Sammlungen ein, andernfalls wird der Ruhezustand nicht abgerufen, welche Entität abgerufen werden soll (private Set <Event> -Ereignisse).
  • wenn Sie eine Seite einer bidirektionalen Relation festlegen und die mappedBy-Seite der Relation in derselben Transaktion verwenden möchten, legen Sie beide Seiten fest. Der Ruhezustand wird dies vor dem nächsten Tx nicht tun (wenn die Sitzung eine neue Ansicht aus dem Datenbankstatus ist).

Um den oben genannten Punkt zu behandeln, führen Sie entweder das Speichern in einer Transaktion und das Laden in einer anderen Transaktion durch:

public static void main(String[] args) {
    // init here (getting dao and transaction template)
    transactionTemplate.execute(new TransactionCallback() {
        @Override
        public Object doInTransaction(TransactionStatus status) {
          // save here
        }
    }

    transactionTemplate.execute(new TransactionCallback() {
        @Override
        public Object doInTransaction(TransactionStatus status) {
          // list here
        }
    }
}

oder beide Seiten einstellen:

...
event1.setUser(user);
...
event2.setUser(user);
...
user.setEvents(Arrays.asList(event1,event2));
...

(Vergessen Sie auch nicht, die oben genannten Code-Erweiterungspunkte zu adressieren, "Nicht Liste setzen", "Erfassungstyp").

26
Thierry

Im Falle einer Webanwendung ist es auch möglich, einen speziellen Filter in web.xml zu deklarieren, der Session-per-Request ausführt:

<filter>
    <filter-name>openSessionInViewFilter</filter-name>
    <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>openSessionInViewFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Danach können Sie Ihre Daten jederzeit während der Anfrage lazyloaden.

9
weekens

Ich habe hier nach einem Hinweis auf ein ähnliches Problem gesucht. Ich habe die von Thierry erwähnte Lösung ausprobiert und sie hat nicht funktioniert. Danach habe ich diese Zeilen ausprobiert und es hat funktioniert:

SessionFactory sessionFactory = (SessionFactory) context.getBean("sessionFactory");
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));

Was ich gerade mache, ist ein Batch-Prozess, der die vorhandenen Manager/Services von Spring nutzen muss. Nachdem ich den Kontext geladen und ein paar Aufrufe gemacht hatte, gründete ich die berühmte Ausgabe "konnte keine Sammlung faul initialisieren". Diese 3 Zeilen haben es für mich gelöst.

7
Fran Jiménez

Das Problem ist, dass Ihr Dao eine Ruhezustandsitzung verwendet, aber die verzögerte Auslastung von user.getName (ich nehme an, dass dies der Ort ist, an dem es ausgelöst wird) außerhalb der Sitzung stattfindet - entweder nicht in einer Sitzung oder in einer anderen Sitzung. Normalerweise öffnen wir eine Hibernat-Sitzung vorher - führen DAO-Aufrufe durch und schließen sie nicht, bis alle faulen Ladevorgänge abgeschlossen sind. Webanfragen werden normalerweise in einer großen Sitzung zusammengefasst, sodass diese Probleme nicht auftreten.

Normalerweise haben wir unsere Dao- und Lazy-Aufrufe in einen SessionWrapper eingebunden. Etwas wie das Folgende:

public class SessionWrapper {
    private SessionFactory sessionFactory;
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.hibernateTemplate = new HibernateTemplate(sessionFactory);
    }
    public <T> T runLogic(Callable<T> logic) throws Exception {
        Session session = null;
        // if the session factory is already registered, don't do it again
        if (TransactionSynchronizationManager.getResource(sessionFactory) == null) {
            session = SessionFactoryUtils.getSession(sessionFactory, true);
            TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
        }

        try {
            return logic.call();
        } finally {
            // if we didn't create the session don't unregister/release it
            if (session != null) {
                TransactionSynchronizationManager.unbindResource(sessionFactory);
                SessionFactoryUtils.releaseSession(session, sessionFactory);
            }
        }
    }
}

Offensichtlich hat die SessionFactory dieselbe SessionFactory, die in Ihren Dao eingefügt wurde.


In Ihrem Fall sollten Sie den gesamten Body von ListUserWithEvent in diese Logik einschließen. So etwas wie:

public List listUserWithEvent() {
    return sessionWrapper.runLogic(new Callable<List>() {
        public List call() {
            List users = hibernateTemplate.find("from User");
            for (User user : users) {
                System.out.println("LIST : " + user.getName() + ":");
                user.getEvents().size();
            }
        }
    });
}

Sie müssen die SessionWrapper-Instanz in Ihre Daos einfügen.

3
Gray

Interessant!

Ich hatte das gleiche Problem in der @RequestMapping-Handler-Methode eines @ Controllers .. Die einfache Lösung bestand darin, der Handler-Methode eine @Transactional-Annotation hinzuzufügen, sodass die Sitzung für die gesamte Dauer der Ausführung des Methodenkörpers geöffnet bleibt

2
mhimu

Einfachste Lösung zur Implementierung:

Führen Sie im Rahmen der Sitzung [in der mit @Transactional annotierten API] Folgendes aus:

wenn A eine Liste <B> hatte, die träge geladen ist, rufen Sie einfach eine API auf, die sicherstellt, dass die Liste geladen wird

Was ist das API?

größe(); API der List-Klasse.

Also alles was benötigt wird ist:

Logger.log (a.getBList.size ());

Dieser einfache Aufruf der Protokollierung der Größe stellt sicher, dass die gesamte Liste abgerufen wird, bevor die Größe der Liste berechnet wird. Jetzt bekommen Sie keine Ausnahme!

1
ydntn

Was für uns in JBoss funktionierte, war die Lösung Nr. 2 aus diese Seite bei Java Code Geeks .

Web.xml:

  <filter>
      <filter-name>ConnectionFilter</filter-name>
      <filter-class>web.ConnectionFilter</filter-class>
  </filter>
  <filter-mapping>
      <filter-name>ConnectionFilter</filter-name>
      <url-pattern>/faces/*</url-pattern>
  </filter-mapping>

ConnectionFilter:

import Java.io.IOException;
import javax.annotation.Resource;
import javax.servlet.*;
import javax.transaction.UserTransaction;

public class ConnectionFilter implements Filter {
    @Override
    public void destroy() { }

    @Resource
    private UserTransaction utx;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        try {
            utx.begin();
            chain.doFilter(request, response);
            utx.commit();
        } catch (Exception e) { }
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException { }
}

Vielleicht würde es auch mit Spring funktionieren.

0
EpicPandaForce