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,
DespotEDIT1: (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?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.
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()
.
精彩评论