org.hibernate.LazyInitializationException: Como usar corretamente o recurso de carregamento lento do Hibernate
Eu tive alguns problemas ao carregar uma lista de objetos do meu banco de dados usando o Hibernate e o modo preguiçoso = true. Espero que alguém possa me ajudar aqu
Eu tenho uma classe simples aqui chamada UserAccount que se parece com isso:
public class UserAccount {
long id;
String username;
List<MailAccount> mailAccounts = new Vector<MailAccount>();
public UserAccount(){
super();
}
public long getId(){
return id;
}
public void setId(long id){
this.id = id;
}
public String getUsername(){
return username;
}
public void setUsername(String username){
this.username = username;
}
public List<MailAccount> getMailAccounts() {
if (mailAccounts == null) {
mailAccounts = new Vector<MailAccount>();
}
return mailAccounts;
}
public void setMailAccounts(List<MailAccount> mailAccounts) {
this.mailAccounts = mailAccounts;
}
}
Estou mapeando esta classe no Hibernate através do seguinte arquivo de mapeamento:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="test.account.UserAccount" table="USERACCOUNT">
<id name="id" type="long" access="field">
<column name="USER_ACCOUNT_ID" />
<generator class="native" />
</id>
<property name="username" />
<bag name="mailAccounts" table="MAILACCOUNTS" lazy="true" inverse="true" cascade="all">
<key column="USER_ACCOUNT_ID"></key>
<one-to-many class="test.account.MailAccount" />
</bag>
</class>
</hibernate-mapping>
Como você pode ver, o preguiçoso está definido como "true" no elemento de mapeamento de mala
Salvar os dados no banco de dados funciona bem:
@Loading também funciona chamandoloadUserAccount(String username)
(veja o código abaixo):
public class HibernateController implements DatabaseController {
private Session session = null;
private final SessionFactory sessionFactory = buildSessionFactory();
public HibernateController() {
super();
}
private SessionFactory buildSessionFactory() {
try {
return new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public UserAccount loadUserAccount(String username) throws FailedDatabaseOperationException {
UserAccount account = null;
Session session = null;
Transaction transaction = null;
try {
session = getSession();
transaction = session.beginTransaction();
Query query = session.createQuery("FROM UserAccount WHERE username = :uname").setParameter("uname", username));
account = (UserAccount) query.uniqueResult();
transaction.commit();
} catch (Exception e) {
transaction.rollback();
throw new FailedDatabaseOperationException(e);
} finally {
if (session.isOpen()) {
// session.close();
}
}
return account;
}
private Session getSession() {
if (session == null){
session = getSessionFactory().getCurrentSession();
}
return session;
}
}
O problema é justo: quando eu acesso elementos na lista "mailAccounts", recebo a seguinte exceção:
org.hibernate.LazyInitializationException: falha ao inicializar preguiçosamente uma coleção de funções: test.account.UserAccount.mailAccounts, nenhuma sessão ou sessão foi fechada
Presumo que o motivo dessa exceção seja que a sessão foi fechada (não sei por que e como) e, portanto, o Hibernate não pode carregar a lista. Como você pode ver, eu até removi osession.close()
chamada doloadUserAccount()
, mas a sessão ainda parece ser fechada ou substituída por outra instância. Se eu definirlazy=false
, tudo funciona perfeitamente, mas não é isso que eu queria, porque preciso do recurso de carregar dados "sob demanda" devido a problemas de desempenh
Assim, se não tiver certeza de que minha sessão ainda é válida após o métodoloadUserAccount(String username)
encerrado, qual é o sentido de ter esse recurso e como faço para solucionar isso?
Obrigado pela ajuda
Ps: eu sou iniciante no Hibernate, então desculpe meu noobishnes
Atualizar Aqui está o meu hibernate config.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.password">foo</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mytable</property>
<property name="hibernate.connection.username">user</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
<!-- Auto create tables -->
<!-- <property name="hbm2ddl.auto">create</property>-->
<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>
<!-- Mappings -->
<mapping resource="test/account/SmampiAccount.hbm.xml"/>
<mapping resource="test/account/MailAccount.hbm.xml"/>
</session-factory>
</hibernate-configuration>