2012-07-12 16 views
14

私はhibernateに再帰的にオブジェクトのすべてのプロパティを再帰的に初期化し、オブジェクト全体のグラフがロードされるように1つの関数を書いています。Hibernateの再帰的な初期化

私はカテゴリーとサブカテゴリーのように、この

1)自己の複合オブジェクトを使用する必要がある2つの複雑なシナリオ...

@Entity 
public class Category { 

    @Column(nullable = false, length = 50) 
    @NotNull 
    @Size(max = 50, message = "{50}") 
    protected String name; 

    @ManyToOne(targetEntity = Category.class, fetch = FetchType.LAZY, optional = true) 
    private Category parentCategory; 
    } 

2)にオブジェクトをたくさん持っている複雑なオブジェクトグラフを持っています使用する前に初期化してください。

問題点私はこの全体のオブジェクトグラフが選択的な場合にのみ必要なので、私はeager fetchingを使用できません。一般化されたコードを持ちたいので、オブジェクトのHQLクエリを書く必要はありません。

私はこのための部分的ないくつかのコード、

public void recursiveInitliaze(Object obj) throws Exception { 
    if(!Hibernate.isInitialized(obj)) 
     Hibernate.initialize(obj); 
    PropertyDescriptor[] properties = PropertyUtils.getPropertyDescriptors(obj); 
    for (PropertyDescriptor propertyDescriptor : properties) { 
     Object origProp = PropertyUtils.getProperty(obj, propertyDescriptor.getName()); 
     if (origProp != null) { 
      this.recursiveInitliaze(origProp); 
     } 
     if (origProp instanceof Collection && origProp != null) {    
      for (Object item : (Collection) origProp) { 
       this.recursiveInitliaze(item); 
      } 
     } 
    } 
} 

を書かれているが、それは一つの問題があり、それがあるため、双方向の関係のメソッド呼び出しのためのstackoverflowに終了します。どのように双方向の関係があるかを検出するか、これを実装するためのより良い方法がありますか?

フェッチプロファイルも役立ちますが、プロジェクトの現段階でフェッチプロファイルを構成するのが難しいため、可能であればこれを実装しようとしています。

答えて

7

オブジェクトにHibernate.initialize()を使用して初期化することができます。これがカスケードするかどうかはわかりません。そうでなければ、isInitialisedプロパティを与えるinstanceof HibernateProxyをチェックすることができます。

更新:初期化はカスケードしないので、すでに行ったようにツリーを走査する必要があります。ハッシュセットを使用して、すでに処理したオブジェクトを追跡します。

+0

isInitialised私はすでにロジックで使用しているが、クラスAがBに関連付けられている場合、私が直面しています問題は、例えば、双方向の関係であります両方ともお互いの参照が含まれている場合、私は常にそれをプロパティとして取得し、無限の再帰呼び出しに終わります。 –

+0

isInitialisedは、あなたが一度初期化した後であなたに真実を与えます。その後、カスケードする必要はなく、無限ループはありません。 initialize()があなたが望むことをしているかどうかチェックしましたか? – Firo

+0

はいinitialize()は、現在のオブジェクトのみを初期化し、カスケードしません。私はisInitialisedに依存することはできません。なぜなら、Aのようなオブジェクト階層がBを含んでいるからBはCを含んでいて、Bも初期化されているからです。私のコードの問題は、CにBへの逆参照が含まれている場合、関数を再度呼び出そうとすることです。 –

5

全コード:ReflectionUtilsの

public <T> T recursiveInitliaze(T obj) { 
    Set<Object> dejaVu = Collections.newSetFromMap(new IdentityHashMap<Object, Boolean>()); 
    try { 
     recursiveInitliaze(obj, dejaVu); 
    } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { 
     ReflectionUtils.handleReflectionException(e); 
    } 
    return obj; 
} 

private void recursiveInitliaze(Object obj, Set<Object> dejaVu) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { 
    if (dejaVu.contains(this)) { 
     return; 
    } else { 
     dejaVu.add(this); 

     if (!Hibernate.isInitialized(obj)) { 
      Hibernate.initialize(obj); 
     } 
     PropertyDescriptor[] properties = PropertyUtils.getPropertyDescriptors(obj); 
     for (PropertyDescriptor propertyDescriptor : properties) { 
      Object origProp = PropertyUtils.getProperty(obj, propertyDescriptor.getName()); 
      if (origProp != null) { 
       this.recursiveInitliaze(origProp, dejaVu); 
      } 
      if (origProp instanceof Collection && origProp != null) { 
       for (Object item : (Collection<?>) origProp) { 
        this.recursiveInitliaze(item, dejaVu); 
       } 
      } 
     } 
    } 
} 

コード:

/** 
* Handle the given reflection exception. Should only be called if no 
* checked exception is expected to be thrown by the target method. 
* <p>Throws the underlying RuntimeException or Error in case of an 
* InvocationTargetException with such a root cause. Throws an 
* IllegalStateException with an appropriate message else. 
* @param ex the reflection exception to handle 
*/ 
public static void handleReflectionException(Exception ex) { 
    if (ex instanceof NoSuchMethodException) { 
     throw new IllegalStateException("Method not found: " + ex.getMessage()); 
    } 
    if (ex instanceof IllegalAccessException) { 
     throw new IllegalStateException("Could not access method: " + ex.getMessage()); 
    } 
    if (ex instanceof InvocationTargetException) { 
     handleInvocationTargetException((InvocationTargetException) ex); 
    } 
    if (ex instanceof RuntimeException) { 
     throw (RuntimeException) ex; 
    } 
    throw new UndeclaredThrowableException(ex); 
} 
関連する問題