2

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 メソッドで永続化された User オブジェクトから Address を取得しようとすると、 LazyInitializationException が発生します)。

そのため、Spring と Hibernate を使用したトランザクション管理について読み始めました。stackoverflow でいくつかのスレッドを読みましたが、実装には遭遇していません。私も読んだ:
http://community.jboss.org/wiki/Sessionsandtransactions
http://community.jboss.org/wiki/OpenSessioninView
http://community.jboss.org/wiki/SessionhandlingwithAOP
Spring リファレンス - 9.5。宣言型トランザクション管理
Spring リファレンス - 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>

アプリケーションのある時点でユーザーを取得した後、ユーザー オブジェクトのアドレスにアクセスしないため、処理は正常に行われます。いくつかのリクエストとレスポンスが行き来することを想像してみてください。特定の要求よりも (重要ではありませんが、ユーザーはオプションを選択して送信ボタンをクリックします)、処理は checkout.OrderService.ECSetExpressCheckoutCode メソッドにつながります。ここで、OrderService.java : 274 行に次の行があります。

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

この行に入ろうとすると、LazYInitException が発生します。これは、Address クラスがデフォルトの lazy="true" 属性でマップされているためです。
これで十分な説明ですか?問題は、正しい Spring Hibernate Declarative Transaction Management 構成は何ですか?

4

2 に答える 2

0

Countryへの 1 回の呼び出しで DB からオブジェクトを取得OrderServiceし、別の への呼び出しでそのオブジェクトにアクセスしようとしていますOrderServiceか? 同じセッションを維持している限り、プロキシしか使用できないと思います。

とにかく、完全なコードを提供する必要があります。

于 2010-12-02T21:38:59.873 に答える
0

簡単な方法: getUserForUserId()DAO のメソッドを変更して、2 つのアドレスでフェッチ結合を行うか、または を呼び出したトランザクション中に少なくとも 1 回は 2 つのアドレス オブジェクトにアクセスしますgetUserForUserId()

難しい方法: Hibernate マニュアルの第 13 章でデタッチされたオブジェクトについて読み、低レベルの Hibernate メソッドを使用して User オブジェクトを再アタッチします (例: merge())。もちろん、そのためにはセッションが必要ですが、エラー メッセージにはセッションがないと表示されます。

簡単な方法は、コードがおそらく想定していることをサポートしていuser.getShippingAddress()ますgetUserForUserId()

于 2011-06-17T22:24:45.150 に答える