我的Spring Hibernate事务管理配置有什么问题?

时间:2010-12-02 21:18:44

标签: hibernate spring transactions session-management

我正在使用Spring 2.5.5。 Hibernate3.2的。和MySQL。
我得到了:

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)

这部分我理解( Hibernate为Address而不是真正的目标设置代理,当我尝试从checkout.OrderService.ECSetExpressCheckoutCode方法中的持久User对象获取地址时,我得到了LazyInitializationException )。

这就是我开始阅读Spring和Hibernate的事务管理的原因。我在stackoverflow上读了几个线程,但是我没有遇到任何实现。我也读过:
http://community.jboss.org/wiki/Sessionsandtransactions
http://community.jboss.org/wiki/OpenSessioninView
http://community.jboss.org/wiki/SessionhandlingwithAOP
春季参考 - 9.5。声明式交易管理
春季参考 - 12.2.7。声明性交易划分
等等 我的事务管理配置(按照说明)如下所示:

 <!-- =============================== 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 ========================= -->

我再次收到同样的错误!
我不确定这个配置是否与我拥有的Dao层实现发生冲突(但它不应该这样):

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

}

据我所知,要让Address代理从DB获取“真正的”目标,我需要提供一个Hibernate会话和事务。我读过时有几种模式,但对我来说最方便的是声明方法让我的方法划分界限(从事务逻辑“干净”)。 那么我的配置中会错过什么?请记住,我不仅需要DAO层中的事务管理,还需要服务层(可能还有控制器)。 如何使这项工作?

亲切的问候,
暴君

EDIT1 :(由于要求提供更多信息)首先看一下AccountDao,方法是getUserForUserId。 这是我从应用程序的一个方面调用的方法。因此,您可以了解用户是什么以及如何映射,我为您提供以下POJO:

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

和映射文件:

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

在应用程序的一个点上获取User后,处理流程正常,因为我没有访问User对象中的Address。因此,想象一下请求和响应的来回。比某个请求出现(不是很重要,但用户选择一个选项并点击提交按钮)并且处理导致checkout.OrderService.ECSetExpressCheckoutCode方法,其中在OrderService.java:274我有

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

一旦我尝试输入这一行,我就会得到LazYInitException,因为Address类是使用默认的lazy =“true”属性映射的。
这个说明够了吗? 问题是什么是正确的Spring Hibernate声明式事务管理配置?

2 个答案:

答案 0 :(得分:0)

您是在通过对Country的一次通话中从数据库中检索OrderService对象,然后尝试在另一次调用OrderService时访问该对象吗?我认为只要你保持相同的会话,你就只能使用代理。

无论如何,你应该提供完整的代码。

答案 1 :(得分:0)

简单方法:更改DAO中的getUserForUserId()方法以在两个地址上执行提取连接,或者仅在仍调用getUserForUserId()的事务中访问两个地址对象。

困难的方法:阅读chapter 13 of the Hibernate manual中的分离对象,并使用低级Hibernate方法重新附加User对象(例如merge())。当然,为此,您需要一个会话,并且您的错误消息表明您没有会话。

简单的方法支持您的代码可能假定的内容:在调用user.getShippingAddress()后随时调用getUserForUserId()