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?