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

Posted

技术标签:

【中文标题】我的 Spring Hibernate 事务管理配置有啥问题?【英文标题】:What is wrong with my Spring Hibernate Transaction Management Configuration?我的 Spring Hibernate 事务管理配置有什么问题? 【发布时间】:2011-05-19 09:49:06 【问题描述】:

我使用的是 Spring 2.5.5。休眠3.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 方法中的持久用户对象获取地址时,我得到 LazyInitializationException )。

这就是我开始阅读有关 Spring 和 Hibernate 的事务管理的原因。我在 *** 上阅读了几个线程,但我没有遇到任何实现。我还阅读了:http://community.jboss.org/wiki/Sessionsandtransactionshttp://community.jboss.org/wiki/OpenSessioninViewhttp://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。 这是我从应用程序的某一点调用的方法。 为了让您了解 User 是什么以及它是如何映射的,我为您提供了以下 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>

在应用程序的某一点获取用户后,处理流程正常,因为我没有访问用户对象中的地址。所以想象一下几个请求和响应来回走动。然后出现某个请求(不是说它很重要,而是用户选择一个选项并单击提交按钮)并且处理导致 checkout.OrderService.ECSetExpressCheckoutCode 方法在 OrderService.java:274 行我有强>:

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

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

【问题讨论】:

hibernate: LazyInitializationException: could not initialize proxy的可能重复 hvgotcodes,我已经阅读了那篇文章。首先的问题是他能做什么,这样他就不会得到错误。我已经知道我需要做什么,但无法弄清楚正确的配置!他最后提到的只是“我已经从所有 DAO 方法中提取了所有事务代码,并在应用程序/管理器层严格设置了事务”。这就是我想要达到的目标。所以我不相信这是重复的。 【参考方案1】:

您是否在一次调用 OrderService 时从 DB 中检索您的 Country 对象,然后尝试在另一次调用 OrderService 时访问它?我认为你只能使用代理,只要你保持相同的会话。

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

【讨论】:

首先,当尝试从 User 对象中获取 Address 对象时抛出异常(不是我之前认为的 Country,但这并不重要)。我在应用程序的某一点(不是 OrderService)从 AccountDao 调用 getUserForUserId。那部分没问题。在几个请求(和响应)之后,我到达了 OrderService 方法,当我尝试从用户那里获取地址时,我得到了异常。 (见 EDIT1) “我认为你只能使用代理,只要你保持相同的会话。”好的,但我认为我正在使用我提供的事务管理配置来做到这一点。你是说通过使用“this.hibernateTemplate.execute(new HibernateCallback() public Object doInHibernate(Session session) ”,我(Hibernate 模板)打开和关闭另一个会话?这是问题吗?我仍然认为我的事务管理配置缺少一些东西。【参考方案2】:

简单的方法:更改 DAO 中的 getUserForUserId() 方法以对两个地址进行 fetch join,或者在仍处于调用 getUserForUserId() 的事务中时至少访问两个地址对象一次。

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

简单的方法支持您的代码可能假设的内容:在调用getUserForUserId() 之后的任何时间调用user.getShippingAddress()

【讨论】:

以上是关于我的 Spring Hibernate 事务管理配置有啥问题?的主要内容,如果未能解决你的问题,请参考以下文章

Hibernate Interceptor 中自动装配字段的空指针异常(类由 Spring 管理)

Hibernate 和 Spring:尝试创建事务时出现异常

我的java项目中spring事务总是出问题?

使用 Hibernate 和 Spring 进行批量插入

Spring Hibernate:Generic Dao 添加原因 - org.hibernate.TransactionException:不支持嵌套事务

Bitronix + Spring + Hibernate + Persistence