Pour un défilement de réponse jusqu'à la fin de cette ...Hibernate/Spring: Échec de l'initialisation paresseusement - aucune session ou session fermée
Le problème de base est le même que le temps multiple demandé. J'ai un programme simple avec deux POJOs Event et User - où un utilisateur peut avoir plusieurs événements.
@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;}
}
L'Utilisateur:
@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; }
}
Note: Ceci est un exemple de projet. Je vraiment voulez utiliser Lazy fetching ici.
Maintenant, nous devons configurer le printemps et mise en veille prolongée et un simple db.xml de base pour le chargement:
<?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>
Note: J'ai joué avec le CustomScopeConfigurer et SimpleThreadScope, mais cela ne change rien.
J'ai simple dao-impl (seulement coller le userDao - le EventDao est à peu près la même chose - sauf avec la fonction "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;
}
}
Je reçois le org.hibernate.LazyInitializationException - Impossible d'initialiser paresseusement une collection de rôle: data.model.User.events, aucune session ou séance est levée à la ligne avec user.getEvents() taille();
et last but not least ici. est la classe de test que j'utilise:
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();
}
}
et voici l'exception:
1621 [main] ERROR org.hibernate.LazyInitializationException - failed to lazily initialize a collection of role: data.model.User.events, no session or session was closed org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: data.model.User.events, no session or session was closed at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:380) at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:372) at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:119) at org.hibernate.collection.PersistentBag.size(PersistentBag.java:248) at data.dao.impl.UserDaoImpl.listUserWithEvent(UserDaoImpl.java:38) at HibernateTest.main(HibernateTest.java:44) Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: data.model.User.events, no session or session was closed at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:380) at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:372) at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:119) at org.hibernate.collection.PersistentBag.size(PersistentBag.java:248) at data.dao.impl.UserDaoImpl.listUserWithEvent(UserDaoImpl.java:38) at HibernateTest.main(HibernateTest.java:44)
Les choses ont essayé mais ne fonctionnent pas:
- attribuer un threadScope et en utilisant BeanFactory (je "demande" ou "fil" - pas de différence remarqué):
// scope stuff Scope threadScope = new SimpleThreadScope(); ConfigurableListableBeanFactory beanFactory = ac.getBeanFactory(); beanFactory.registerScope("request", threadScope); ac.refresh(); ...
- Mise en place d'une transaction en obtenant l'objet de la session du deo:
... Transaction tx = ((UserDaoImpl)udao).getSession().beginTransaction(); tx.begin(); users = udao.listUserWithEvent(); ...
- obtenir une transaction dans le listUserWithEvent()
public List listUserWithEvent() { SessionFactory sf = hibernateTemplate.getSessionFactory(); Session s = sf.openSession(); Transaction 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(); return users; }
Je suis vraiment d'idées maintenant . En outre, l'utilisation de listUser ou listEvent fonctionne correctement.
Un pas en avant:
Merci à Thierry je suis arrivé un peu plus loin (je pense). J'ai créé la classe MyTransaction et je fais tout mon travail là-bas, obtenant tout à partir du printemps.Les nouveaux looks principaux comme ceci:
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();
}
Malheureusement maintenant, il y a une exception de pointeur NULL @:. User.getEvents() taille(); (dans le daoImpl). Je sais qu'il ne devrait pas être nul (ni de la sortie dans la console ni de la mise en page db).
Voici la sortie de la console pour plus d'informations (je l'ai fait un chèque de user.getEvent() == null et imprimé "EVENT est NULL"):
New user... Hibernate: insert into User (name) values (?) Hibernate: insert into User (name) values (?) Hibernate: insert into Event (name, user_id) values (?, ?) Hibernate: insert into Event (name, user_id) values (?, ?) Hibernate: insert into Event (name, user_id) values (?, ?) List users: Hibernate: select user0_.id as id0_, user0_.name as name0_ from User user0_ 1:User1 2:User2 List events: Hibernate: select event0_.id as id1_, event0_.name as name1_, event0_.user_id as user3_1_ from Event event0_ 1:Birthday1 for 1:User1 2:Birthday2 for 1:User1 3:Wedding for 2:User2 Hibernate: select user0_.id as id0_, user0_.name as name0_ from User user0_ Events for users 1:User1 -- EVENT is NULL 2:User2 -- EVENT is NULL
Vous pouvez obtenir l'exemple de projet de http://www.gargan.org/code/hibernate-test1.tgz (il est une éclipse/projet Maven)
La solution (pour les applications console)
il y a en fait deux solutions à ce problème - en fonction de votre environnement:
Pour une application console vous avez besoin d'un modèle de transaction qui capture la logique db actutal et prend soin de la transaction:
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;
}
}
Vous pouvez utiliser en appelant:
TransactionTemplate transactionTemplate = (TransactionTemplate) context.getBean("transactionTemplate");
UserGetTransaction mt = new UserGetTransaction(context);
transactionTemplate.execute(mt);
Pour Pour travailler, vous devez définir la classe de modèle pour le printemps (ie. dans votre-db.xml de base):
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
Une autre solution
merci 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);
}
}
(possible) La solution (pour les servlets)
servlets pas ce gros problème. Lorsque vous avez un servlet, vous pouvez simplement commencer et lier une transaction au début de votre fonction et délie à nouveau à la fin:
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);
}
J'ai essayé votre solution, et je me suis débarrassé du problème de Lazy - malheureusement, il ne charge aucun événement: user.getEvents() Produit un pointeur nul (bien que je puisse voir dans le db et depuis le premier itération que l'utilisateur a des événements associés). Est-il possible que hibernateTemplate.find doe snot résolve les dépendances? – Niko
J'ai téléchargé mon test-projet avec votre suggestion déjà en place. peut-être que vous pouvez découvrir ce qui se passe. – Niko