java - org.hibernate.LazyInitializationException: How to properly use Hibernate's lazy loading feature -


i got trouble loading list of objects database using hibernate , lazy=true mode. hope can me out here.

i have simple class here called useraccount looks this:

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

i mapping class in hibernate via following mapping file:

<?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> 

as can see, lazy set "true" in bag mapping element.

saving data database works fine:

loading works calling loaduseraccount(string username) (see code below):

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 username = :uname").setparameter("uname", username));             account = (useraccount) query.uniqueresult();             transaction.commit();         } catch (exception e) {             transaction.rollback();             throw new faileddatabaseoperationexception(e);         } {             if (session.isopen()) {                 // session.close();             }         }          return account;     }      private session getsession() {         if (session == null){             session = getsessionfactory().getcurrentsession();         }         return session;     } } 

the problem just: when access elements within list "mailaccounts", following exception:

org.hibernate.lazyinitializationexception: failed lazily initialize collection of role: test.account.useraccount.mailaccounts, no session or session closed

i assume reason exception session got closed (don't know why , how) , hibernate cannot load list. can see, removed session.close() call loaduseraccount() method session still seems either closed or replaced instance. if set lazy=false, works smoothly not wanted because need feature of loading data "on demand" due performance issues.

so, if can't sure session still valid after method loaduseraccount(string username) terminated, what's point of having feature , how work around that?

thanks help!

ps: hibernate beginner please excuse noobishness.

update: here 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> 

lazy loading working or not has nothing transaction boundaries. requires session open.

however, when session open depends on how you've set sessionfactory, did not tell us! there config going on behind sessionfactory.getcurrentsession() does! if you're letting go default version of threadlocalsessioncontext , not doing manage life cycle, indeed default closing session when commit. (hence common conception broadening transaction boundaries 'fix' lazy load exception.)

if manage own session life cycle sessionfactory.opensession() , session.close() able lazy load fine within session life cycle, outside transaction boundaries. alternately can provide subclass of threadlocalsessioncontext manages session life-cycle boundaries desire. there readily available alternatives such opensessioninview filter can used in web applications bind session life-cycle web request life cycle.

edit: can of course initialize list inside transaction if works you. think leads clunky apis when need either new method signature of kind of 'flag' parameter each level of hydration of entity. dao.getuser() dao.getuserwithmailaccounts() dao.getuserwithmailaccountsandhistoricalids() , on.

edit 2: may find helpful different approaches how long session stays open/the relationship between session scope , transaction scope. (particularly idea of session-per-request-with-detached-objects vs session-per-conversation.)

it depends on requirements , architecture how big conversation is.


Comments