2

I am using Spring 2.5.5. Hibernate3.2. and MySQL.
I was getting:

org.hibernate.LazyInitializationException: could not initialize proxy - no Session
 at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:57)
 at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:111)
 at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:150)
 at beans.Country$$EnhancerByCGLIB$$e757f40e.getStringId(<generated>)
 at beans.Address.getCountryStringId(Address.java:137)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
 at java.lang.reflect.Method.invoke(Unknown Source)
 at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:157)
 at beans.Address$$EnhancerByCGLIB$$ef3e9848.getCountryStringId(<generated>)
 at checkout.OrderService.ECSetExpressCheckoutCode(OrderService.java:274)

This part I understand (Hibernate put a proxy for Address instead of the real target and when I try to get the Address from the persisted User object in the checkout.OrderService.ECSetExpressCheckoutCode method I get the LazyInitializationException).

That is why I started reading about transaction management with Spring and Hibernate. I read couple of threads at stackoverflow but I haven't encountered any implementation. I read also:
http://community.jboss.org/wiki/Sessionsandtransactions
http://community.jboss.org/wiki/OpenSessioninView
http://community.jboss.org/wiki/SessionhandlingwithAOP
Spring reference - 9.5. Declarative transaction management
Spring reference - 12.2.7. Declarative transaction demarcation
and more.
My transaction management configuration (following the instructions) looks like this:

 <!-- =============================== TRANSACTION MANAGMENT ============================= -->
 <!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean below) -->
 <tx:advice id="txAdvice" transaction-manager="txManager">
  <!-- the transactional semantics... -->
  <tx:attributes>
   <!-- all methods starting with 'get' are read-only -->
   <tx:method name="get*" read-only="true"/>
   <!-- other methods use the default transaction settings (see below) -->
   <tx:method name="*"/>
  </tx:attributes>
 </tx:advice>

 <aop:config>
  <aop:pointcut id="orderServiceOperation" expression="execution(* checkout.OrderService.*(..))"/>
  <aop:advisor advice-ref="txAdvice" pointcut-ref="orderServiceOperation"/>
 </aop:config>
 <bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
  <property name="sessionFactory">
              <ref local="sessionFactory"/>
  </property>
 </bean>
 <!-- ============================ END OF TRANSACTION MANAGMENT ========================= -->

I get the same error again!
I am not sure if this configuration collides with the Dao layer implementation that I have (but it shouldn't supposed to):

public class AccountDao implements AccountDaoInterface {

 private HibernateTemplate hibernateTemplate;

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

 public User getUserForUserId(final long ruserId)  throws DataAccessException {
  return (User) this.hibernateTemplate.execute(new HibernateCallback() {
   public Object doInHibernate(Session session) {
    List objList = session.createQuery("from User where id=" + userId).list();
    User user = null;
    if (!objList.isEmpty()){
     user = (User) objList.get(0);
    }
    return user;
   }
  });
 }

 //other methods

}

I understand that for the Address proxy to get the "real" target from DB, I need to provide a Hibernate session and transaction. There are several patterns as I read, but the most convenient for me is the declarative approach leaving my methods demarcated ("clean" from transaction logic). So what do I miss in my config? Remember I not only need the transaction management in the DAO layer but also the Service layer (and maybe the controllers). How can I make this work?

Kind regards,
Despot

EDIT1: (due to request for more info) First of all look at the AccountDao, the method getUserForUserId. This is the method I call from one point of the application. So you can understand what the User is and how it is mapped, I provide you the following POJOs:

public class User implements Serializable{
    private long id;
    private Address shippingAddress;
    private Address billingAddress;
    private String otherData;
    //default constructor, constructor using fields, getters, setters.
}
public class Address implements Serializable{
    private long id;
    private Country country;
    private String otherData;
    //default constructor, constructor using fields, getters, setters.
}
public class Country implements Serializable {
    int id;
    String stringId;
    String name;
}

and the mapping files:

<class name="User" table="User">
    <id name="id" column="id" type="long" unsaved-value="-1">
        <generator class="native" />
    </id>
    <many-to-one name="shippingAddress" class="beans.Address" column="shippingAddressId" not-null="true" cascade="persist,merge,save-update"/>
    <many-to-one name="billingAddress" class="beans.Address" column="billingAddressId" not-null="true" cascade="persist,merge,save-update"/>
</class>
<class name="Address" table="Address">
    <id name="id" column="id" type="long">
        <generator class="native" />
    </id>
    <many-to-one name="country" class="Country" column="countryId" not-null="true" cascade="none"  />
</class>
<class name="Country" table="Country">
    <id name="id" column="id" type="integer">
        <generator class="native" />
    </id>
    <property name="stringId" type="string" not-null="true" length="4"/>
    <property name="name" type="string" not-null="true" length="100"/>
</class>

After I get the User in one point of the application, the processing flows ok since I don't access the Address in the User object. So imagine couple of requests and responses go back and forth. Than a certain request comes along (not that it is important but, the user chooses an option and clicks on a submit button) and the processing leads to the checkout.OrderService.ECSetExpressCheckoutCode method where at line OrderService.java:274 I have:

user.getShippingAddress().getCountryStringId().equals(AddressConst.COUNTRY_US_STRING_ID)

Once I try to enter this line I get the LazYInitException, since the Address class is mapped with the default lazy="true" attribute.
Is this enough explanation? The question is what is the correct Spring Hibernate Declarative Transaction Management configuration?

despot
  • 7,167
  • 9
  • 44
  • 63
  • possible duplicate of [hibernate: LazyInitializationException: could not initialize proxy](http://stackoverflow.com/questions/345705/hibernate-lazyinitializationexception-could-not-initialize-proxy) – hvgotcodes Dec 02 '10 at 21:23
  • hvgotcodes, I already read that post. First of all the question there is what he can do so he doesn't get the error. I already know what I need to do, but can't figure out the correct configuration! All he mentions in the end is " I've hauled all the transaction code from all the DAO methods and have set up transactions strictly at the application/manager layer". This is what I am trying to achieve. So I don't believe this is a duplicate. – despot Dec 03 '10 at 09:36

2 Answers2

0

Are you retrieving your Country object from DB in one call to the OrderService and then trying to access it in another call to OrderService? I think you can only use proxies as long as you maintain the same session.

Anyway you should provide the complete code.

gpeche
  • 21,974
  • 5
  • 38
  • 51
  • First of all, the exception is thrown when trying to get the Address object from the User object (not the Country as I thought previously, but that is not so important). I call get getUserForUserId from AccountDao in one point of the application (not OrderService). That part is ok. After couple of requests (and responses), I reach the OrderService method and when I try to get the Address from the User, I get the exception. (See EDIT1) – despot Dec 03 '10 at 10:37
  • "I think you can only use proxies as long as you maintain the same session." Ok, but I thought I was doing that with the transaction management configuration that I provided. Are you saying that by using "this.hibernateTemplate.execute(new HibernateCallback() { public Object doInHibernate(Session session) {", I (the Hibernate Template) opens and closes another session? Is that the problem? I still think that my transaction management configuration is missing something. – despot Dec 03 '10 at 10:46
0

Easy way: Change the getUserForUserId() method in your DAO to do a fetch join on the two addresses, or just access the two address objects at least once while still in the transaction that called getUserForUserId().

Hard way: Read about detached objects in chapter 13 of the Hibernate manual and use a low-level Hibernate method to reattach the User object (e.g. merge()). Of course, to do that you'll need a session, and your error message says you don't have one.

The easy way supports what your code probably assumes: that it call user.getShippingAddress() anytime after a call to getUserForUserId().

gatkin
  • 1,902
  • 12
  • 12