2009-05-21 5 views
1

私のインターセプターでgetEntityNameメソッドを実装しました。私はオブジェクトを保存するときに(一時的な)オブジェクトの実体名を解決するためにHibernateによってメソッドが呼び出されることを期待していました。ただし、インターセプタのgetEntityNameメソッドは、次のシナリオでは使用されませんでした。インターセプタのgetEntityNameは使用されません。休止状態のバグ?

1)session.saveOrUpdateがいくつかのエンティティ名の値で呼び出されました。エンティティ名がインターセプタによって上書きされることが予想されました。 セーブメソッド呼び出しからエンティティ名を削除したときに、インターセプタのgetEntityNameが使用されたという興味深い点があります。これは、saveがエンティティ名なしで呼び出された場合にのみ使用されるように見えます。

2)コレクション内のオブジェクトはカスケード時に保存され、コレクションマッピングの1対多の関連付けがentity-nameを別のクラスマッピングの参照として使用しました。私は、このアソシエーションのエンティティ名がシナリオ1と同様にカスケードに使用されたことを信じています。

誰かがHibernateのバグであるかどうかを教えてもらえますか?

以下は私のマッピングです。 Hibernateのバージョンは3.3.1.GAです。

<class name="User" table="HUBUSER"> 
<id name="id" type="integer"> 
    <column name="USERID"/> 
    <generator class="sequence"> 
    <param name="sequence">USERID</param> 
    </generator> 
</id> 
... 
<map cascade="all" inverse="true" name="attributes"> 
    <key on-delete="cascade" update="false"> 
    <column name="USERID"/> 
    </key> 
    <map-key column="PROPERTYKEY" type="string"/> 
    <one-to-many class="HBAttribute" entity-name="USERPROPERTY"/> 
</map> 
... 
</class> 



<class discriminator-value="HBAttribute" entity-name="USERPROPERTY" name="HBAttribute" table="USERPROPERTY"> 
<id name="id" type="integer"> 
    <column name="USERPROPERTYID"/> 
    <generator class="sequence"> 
    <param name="sequence">USERPROPERTYID</param> 
    </generator> 
</id> 
<discriminator column="PROPERTYKEY" type="string"/> 
<property insert="false" name="propertyKey" not-null="false" type="string" update="false"> 
    <column name="PROPERTYKEY"/> 
</property> 
<many-to-one class="User" name="hubObject" not-null="true" update="false"> 
    <column name="USERID"/> 
</many-to-one> 
<!-- Subclass for USERNAME --> 
<subclass discriminator-value="USERNAME" entity-name="USERPROPERTY_USERNAME" name="HBAttributeString"> 
    <property name="value" not-null="false" type="string"> 
    <column name="PROPVALCHAR"/> 
    </property> 
</subclass> 
<!-- Subclass for TITL --> 
<subclass discriminator-value="TITL" entity-name="USERPROPERTY_TITL" name="HBAttributeString"> 
    <property name="value" not-null="false" type="string"> 
    <column name="PROPVALCHAR"/> 
    </property> 
</subclass> 
<!-- Subclass for EMAIL --> 
<subclass discriminator-value="EMAIL" entity-name="USERPROPERTY_EMAIL" name="HBAttributeString"> 
    <property name="value" not-null="false" type="string"> 
    <column length="80" name="PROPVALCHAR"/> 
    </property> 
</subclass> 

この問題に関するあなたの答え/コメントは非常にappriciatedされるだろう。

--------------------更新-------------------------

私は問題が何であるか知っていると思います。

Hibernateは、save/saveOrUpdateで既存のエンティティ名を上書きするためにインターセプタを使用しません。

Hibernateでは、クラスとそのサブクラスのマッピングで識別子とエンティティ名を使用できます。

データベースからレコードをフェッチするときに、サブクラスまたはスーパークラスのマッピングを識別するために使用されます。 次に、わかっている限り、見つかったサブクラスのマッピングに基づいて、永続的なエントリ(オブジェクト)が永続コンテキストで作成されます。 サブクラスに独自のentity-nameがある場合、このentity-nameは永続エントリに割り当てられます。後で 永続オブジェクトを更新するたびに、このentity-nameがサブクラスのマッピングを見つけるためのキーとして使用されます。

上記を踏まえて、Discriminatorは、データベース行をJavaオブジェクト(永続オブジェクト)にマッピングするとき、一方向に動作します。 一時的なJavaオブジェクトのデータベース行へのマッピングについては、オブジェクトのクラス名またはsave(またはsaveOrUpdate)メソッドのentity-nameを使用して、サブクラス/スーパークラスのマッピングを検索します。したがって、ここでのDiscriminatorは使用されません(私はなぜそれが永続オブジェクトの一部ではないのかもしれません)。

私のシナリオでは、HBAttributeクラスマッピングには「USERPROPERTY」エンティティ名があり、そのすべてのサブクラスにも独自のエンティティ名(「USERPROPERTY_USERNAME」、「USERPROPERTY_TITL」および「USERPROPERTY_EMAIL」)があります。 サブクラスのエンティティ名を使用するのは、サブクラスのマッピングのJavaクラスを再利用する必要があるためです。

HBAttributeクラスは、Userクラス(User - > one-to-many - > HBAttribute)と双方向の関連付けを持っています。 ユーザクラスマッピングでは、参照されるクラスマッピングを指定する唯一の方法は、HBAttributeの「USERPROPERTY」エンティティ名をユーザのコレクションの1対多の関連付けに提供することです。コレクションにはcascade = "all"が設定されているため、すべての操作はコレクションのオブジェクトにカスケードされることが予想されます。

ここで問題が発生します。私はUserクラスの一時オブジェクトを作成して、作成したばかりのHBAttributeクラスの一時オブジェクトをコレクションに入れます。したがって、すべてのオブジェクトは一時的です。次に、session.save(user)メソッドでUserオブジェクトを保存すると、属性コレクション内のオブジェクトがカスケードに保存されます。ただし、1対多の関連付けは、 "USERPROPERTY"エンティティ名を使用してHBAttributeクラスのマッピングを参照するため、エンティティ名はカスケード保存メソッドに渡されます。 "USERPROPERTY"エンティティはスーパークラスのマッピングですが、独自のエンティティ名を持つサブクラスがあり、Hibernateはエンティティ名で識別されるサブクラスを解決しません(これはHibernateのコードで実際に認識されます)。Hibernate開発者はDiscriminatorこの場合はこれを行う)。ここで、InterceptorのgetEntityNameは、Hibernateにどのサブクラスentity-nameを使用すべきかを伝えるのに便利ですが、 "USERPROPERTY"エンティティはすでにコレクションマッピングによって設定/提供されているため、インターセプタで上書きする方法はありません。

私の考えは、サブクラスのエンティティ名をHBAttributeオブジェクトに格納し、interceptor.getEntityNameを使用してオブジェクトから取得したエンティティ名を提供することでした。

以下は、インターセプタが元のnot null entity-nameを上書きすることを許可しないjava.org.hibernate.impl.SessionImpl.getEntityPersisterのコードです。

public EntityPersister getEntityPersister(final String entityName, final Object object) { 
    errorIfClosed(); 
    if (entityName==null) { 
     return factory.getEntityPersister(guessEntityName(object)); 
    } 
    else { 
     // try block is a hack around fact that currently tuplizers are not 
     // given the opportunity to resolve a subclass entity name. this 
     // allows the (we assume custom) interceptor the ability to 
     // influence this decision if we were not able to based on the 
     // given entityName 
     try { 
      return factory.getEntityPersister(entityName) 
        .getSubclassEntityPersister(object, getFactory(), entityMode); 
     } 
     catch(HibernateException e) { 
      try { 
       return getEntityPersister(null, object); 
      } 
      catch(HibernateException e2) { 
       throw e; 
      } 
     } 
    } 
} 

これは私のハッキングされたコードです。

public EntityPersister getEntityPersister(final String entityName, final Object object) { 
    errorIfClosed(); 
    if (entityName==null) { 
     return factory.getEntityPersister(guessEntityName(object)); 
    } 
    else { 
     //even if the original entity-name is not null try to resolve 
     //the entity-name via interceptor, if the returned entity-name 
     // is null, then use original entity-name. 
     String overwrittenEntityName = interceptor.getEntityName(object); 
     if (overwrittenEntityName != null) { 
      return factory.getEntityPersister(overwrittenEntityName); 
     } else { 
      // try block is a hack around fact that currently tuplizers are not 
      // given the opportunity to resolve a subclass entity name. this 
      // allows the (we assume custom) interceptor the ability to 
      // influence this decision if we were not able to based on the 
      // given entityName 
      try { 
       return factory.getEntityPersister(entityName) 
         .getSubclassEntityPersister(object, getFactory(), entityMode); 
      } 
      catch(HibernateException e) { 
       try { 
        return getEntityPersister(null, object); 
       } 
       catch(HibernateException e2) { 
        throw e; 
       } 
      } 
     } 
    } 
} 

休止状態でより多くの経験豊富な誰かが私の変更がOKで、休止状態に含まれるように提案する必要があるかどうかを教えてもらえますか?

おかげで、休止状態のコアで アントン

答えて

0

は、サブクラスのエンティティ名の解決は私が私の修正に持っていたよりはるかに優れた方法で実装されています3.3.2。

Tuplizerには、私の場合に上書きできるメソッドdecideConcreteSubclassEntityName(Object entityInstance、SessionFactoryImplementor factory)があります。 Tuplizerクラスはクラスマッピングで登録できます。

あなたのtuplizerに実装して登録できるEntityNameResolver APIもあります。 エンティティ名を解決するためにEntity Interceptorsを使用する必要はありません。