Hibernateプロキシを実際のエンティティオブジェクトに変換する方法


161

Hibernateの実行中に、Session一部のオブジェクトをロードしていますが、一部のオブジェクトは遅延ロードによりプロキシとしてロードされます。すべて問題なく、遅延ロードをオフにしたくありません。

しかし、後で一部のオブジェクト(実際には1つのオブジェクト)をRPC経由でGWTクライアントに送信する必要があります。そして、この具象オブジェクトはプロキシです。だから私はそれを実際のオブジェクトに変える必要があります。Hibernateで「マテリアライズ」のようなメソッドを見つけることができません。

一部のオブジェクトをプロキシからクラスとIDを知っている実数に変えるにはどうすればよいですか?

現時点で私が目にする唯一の解決策は、そのオブジェクトをHibernateのキャッシュから削除して再ロードすることですが、それは多くの理由で本当に悪いです。

回答:


232

これが私が使っている方法です。

public static <T> T initializeAndUnproxy(T entity) {
    if (entity == null) {
        throw new 
           NullPointerException("Entity passed for initialization is null");
    }

    Hibernate.initialize(entity);
    if (entity instanceof HibernateProxy) {
        entity = (T) ((HibernateProxy) entity).getHibernateLazyInitializer()
                .getImplementation();
    }
    return entity;
}

1
私は同じことをしたかったので、プロキシされたインスタンスをObjectOutputStreamに書き込んでから、対応するObjectInputStreamからそれを読み戻しましたが、それでうまくいくように見えました。それが効率的なアプローチであるかどうかはわかりませんが、なぜそれが機能したのか疑問に思っています...それについてのコメントは大歓迎です。ありがとう!
shrini1000

@ shrini1000は、シリアライズするときにコレクションを初期化するために機能しました(セッションがまだ閉じられていない場合)。またHibernateProxywriteReplaceシリアライゼーション中に実装者に特別なことを強制するメソッドも定義します。
Bozho

1
これを行うポータブル(JPA)の方法はありますか?
Kawu

なぜ、Hibernate.initializeが呼び出し時にlazyInitializeExceptionをスローするのですか?私は次のように使用しています:Object o = session.get(MyClass.class、id); Object other = o.getSomeOtherClass(); initializeAndUnproxy(その他);
fredcrs 2012年

6
独自のutilクラスがなくても同じことができます(T)Hibernate.unproxy(entity)
panser

46

この記事で説明したように、Hibernate ORM 5.2.10以降、次のように実行できます。

Object unproxiedEntity = Hibernate.unproxy(proxy);

Hibernate 5.2.10より前。これを行う最も簡単な方法は、Hibernate内部実装によって提供されるunproxyメソッドを使用することでしたPersistenceContext

Object unproxiedEntity = ((SessionImplementor) session)
                         .getPersistenceContext()
                         .unproxy(proxy);

親エンティティでこれを呼び出すと、コレクションフィールドが処理されますか?たとえば、DepartmentwithのListがあるStudent場合でも、それを行う必要がありますか、それともunproxy(department.getStudents()) 十分unproxy(department)ですか?
トラファルマドリアン

1
指定されたプロキシのみが初期化されます。ルートエンティティをプロキシ解除すると、大量のデータが読み込まれる可能性があるため、関連付けにカスケードされません。
Vlad Mihalcea

ただしPersistentContext#unproxy(proxy)、プロキシが初期化されていない場合は例外をスローし、必要に応じてプロキシHibernate.unproxy(proxy)LazyInitializer#getImplementation(proxy)初期化します。この違いにより、例外が発生しました。;-)
bgraves


13

プロキシからオブジェクトをクリーンアップする次のコードを記述しました(まだ初期化されていない場合)

public class PersistenceUtils {

    private static void cleanFromProxies(Object value, List<Object> handledObjects) {
        if ((value != null) && (!isProxy(value)) && !containsTotallyEqual(handledObjects, value)) {
            handledObjects.add(value);
            if (value instanceof Iterable) {
                for (Object item : (Iterable<?>) value) {
                    cleanFromProxies(item, handledObjects);
                }
            } else if (value.getClass().isArray()) {
                for (Object item : (Object[]) value) {
                    cleanFromProxies(item, handledObjects);
                }
            }
            BeanInfo beanInfo = null;
            try {
                beanInfo = Introspector.getBeanInfo(value.getClass());
            } catch (IntrospectionException e) {
                // LOGGER.warn(e.getMessage(), e);
            }
            if (beanInfo != null) {
                for (PropertyDescriptor property : beanInfo.getPropertyDescriptors()) {
                    try {
                        if ((property.getWriteMethod() != null) && (property.getReadMethod() != null)) {
                            Object fieldValue = property.getReadMethod().invoke(value);
                            if (isProxy(fieldValue)) {
                                fieldValue = unproxyObject(fieldValue);
                                property.getWriteMethod().invoke(value, fieldValue);
                            }
                            cleanFromProxies(fieldValue, handledObjects);
                        }
                    } catch (Exception e) {
                        // LOGGER.warn(e.getMessage(), e);
                    }
                }
            }
        }
    }

    public static <T> T cleanFromProxies(T value) {
        T result = unproxyObject(value);
        cleanFromProxies(result, new ArrayList<Object>());
        return result;
    }

    private static boolean containsTotallyEqual(Collection<?> collection, Object value) {
        if (CollectionUtils.isEmpty(collection)) {
            return false;
        }
        for (Object object : collection) {
            if (object == value) {
                return true;
            }
        }
        return false;
    }

    public static boolean isProxy(Object value) {
        if (value == null) {
            return false;
        }
        if ((value instanceof HibernateProxy) || (value instanceof PersistentCollection)) {
            return true;
        }
        return false;
    }

    private static Object unproxyHibernateProxy(HibernateProxy hibernateProxy) {
        Object result = hibernateProxy.writeReplace();
        if (!(result instanceof SerializableProxy)) {
            return result;
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    private static <T> T unproxyObject(T object) {
        if (isProxy(object)) {
            if (object instanceof PersistentCollection) {
                PersistentCollection persistentCollection = (PersistentCollection) object;
                return (T) unproxyPersistentCollection(persistentCollection);
            } else if (object instanceof HibernateProxy) {
                HibernateProxy hibernateProxy = (HibernateProxy) object;
                return (T) unproxyHibernateProxy(hibernateProxy);
            } else {
                return null;
            }
        }
        return object;
    }

    private static Object unproxyPersistentCollection(PersistentCollection persistentCollection) {
        if (persistentCollection instanceof PersistentSet) {
            return unproxyPersistentSet((Map<?, ?>) persistentCollection.getStoredSnapshot());
        }
        return persistentCollection.getStoredSnapshot();
    }

    private static <T> Set<T> unproxyPersistentSet(Map<T, ?> persistenceSet) {
        return new LinkedHashSet<T>(persistenceSet.keySet());
    }

}

私は、RPCサービスの結果に対して(アスペクトを介して)この関数を使用し、プロキシからすべての結果オブジェクトを再帰的にクリーンアップします(初期化されていない場合)。


すべてのユースケースをカバーしているわけではありませんが、このコードを共有していただきありがとうございます。本当に役に立ちます...
Prateek Singh

正しい。新しいケースに応じて更新する必要があります。あなたはGWTの人たちが推奨するものを試すことができます。ここを見てください:gwtproject.org/articles/using_gwt_with_hibernate.html(統合戦略のパートを参照)。一般的に、彼らはDTOまたはDozerまたはGileadの使用を推奨しています。これについてご意見をいただければ結構です。私の場合、それは私のコードは、最も簡単な解決策が、完全ではない=(あるに見えます。
セルゲイBondarev

ありがとう。「CollectionsUtils.containsTotallyEqual(handledObjects、value)」の実装はどこで取得できますか?
Ilan.K 2015

public static boolean containsTotallyEqual(Collection <?> collection、Object value){if(isEmpty(collection)){return false; } for(Object object:collection){if(object == value){return true; }} falseを返します。}
Sergey Bondarev

自分で作成した実用的な方法です
セルゲイボンダレフ

10

JPA 2で私が推奨する方法:

Object unproxied  = entityManager.unwrap(SessionImplementor.class).getPersistenceContext().unproxy(proxy);

2
あなたの答えは私の答えとどう違うのですか?
Vlad Mihalcea 2016年

私はこの解決策を試しました... unwrapコマンドの前に次のようなものを置かないと、常に機能するわけではありません。HibernateProxy hibernateProxy =(HibernateProxy)possibleProxyObject; if(hibernateProxy.getHibernateLazyInitializer()。isUninitialized()){hibernateProxy.getHibernateLazyInitializer()。initialize(); }
user3227576 2017

2

Spring Data JPAとHibernateでは、のサブインターフェースをJpaRepository使用して、「結合」戦略を使用してマップされたタイプ階層に属するオブジェクトを検索していました。残念ながら、クエリは期待される具象型のインスタンスではなく、基本型のプロキシを返していました。これにより、結果を正しい型にキャストできませんでした。あなたのように、私は私のエンティティをプロキシしないようにする効果的な方法を探してここに来ました。

ヴラドは、これらの結果を代用するための正しい考えを持っています。ヤニスはもう少し詳細を提供します。彼らの答えに加えて、あなたが探しているかもしれない残りのものはここにあります:

次のコードは、プロキシされたエンティティをプロキシ解除する簡単な方法を提供します。

import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SessionImplementor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.JpaContext;
import org.springframework.stereotype.Component;

@Component
public final class JpaHibernateUtil {

    private static JpaContext jpaContext;

    @Autowired
    JpaHibernateUtil(JpaContext jpaContext) {
        JpaHibernateUtil.jpaContext = jpaContext;
    }

    public static <Type> Type unproxy(Type proxied, Class<Type> type) {
        PersistenceContext persistenceContext =
            jpaContext
            .getEntityManagerByManagedType(type)
            .unwrap(SessionImplementor.class)
            .getPersistenceContext();
        Type unproxied = (Type) persistenceContext.unproxyAndReassociate(proxied);
        return unproxied;
    }

}

プロキシされていないエンティティまたはプロキシされたエンティティをunproxyメソッドに渡すことができます。それらがすでにプロキシされていない場合、それらは単に返されます。それ以外の場合は、プロキシされずに返されます。

お役に立てれば!


1

別の回避策は、呼び出すことです

Hibernate.initialize(extractedObject.getSubojbectToUnproxy());

セッションを閉じる直前。


1

標準のJavaおよびJPA APIを使用してクラスをプロキシ解除するソリューションを見つけました。hibernateでテストされていますが、依存関係としてhibernateを必要とせず、すべてのJPAプロバイダーで動作するはずです。

唯一の要件-親クラス(アドレス)を変更し、単純なヘルパーメソッドを追加するために必要です。

一般的な考え方:自分自身を返すヘルパーメソッドを親クラスに追加します。メソッドがプロキシで呼び出されると、呼び出しは実際のインスタンスに転送され、この実際のインスタンスが返されます。

hibernateはプロキシされたクラスがそれ自体を返し、実際のインスタンスではなくプロキシを返すことを認識するため、実装は少し複雑です。回避策は、返されたインスタンスを、実際のインスタンスとは異なるクラスタイプを持つ単純なラッパークラスにラップすることです。

コードで:

class Address {
   public AddressWrapper getWrappedSelf() {
       return new AddressWrapper(this);
   }
...
}

class AddressWrapper {
    private Address wrappedAddress;
...
}

アドレスプロキシを実際のサブクラスにキャストするには、以下を使用します。

Address address = dao.getSomeAddress(...);
Address deproxiedAddress = address.getWrappedSelf().getWrappedAddress();
if (deproxiedAddress instanceof WorkAddress) {
WorkAddress workAddress = (WorkAddress)deproxiedAddress;
}

あなたのサンプルコードは少し不明瞭に思われます(または多分私はただもっとコーヒーが必要です)。EntityWrapperはどこから来たのですか?それはAddressWrapperである必要がありますか?そして、私はAddressWrappedがAddressWrapperと言うべきだと思いますか?これを明確にできますか?
Gus

@ガス、あなたは正しいです。例を修正しました。ありがとう:)
OndroMih


0

提案された解決策をありがとう!残念ながら、それらのどれも私の場合には機能しませんでした。ネイティブクエリを使用して、JPA-Hibernateを介してOracleデータベースからCLOBオブジェクトのリストを受信しました。

提案されたすべてのアプローチにより、ClassCastExceptionが返されるか、java Proxyオブジェクトが返されました(これには、目的のClobの奥深くに含まれていました)。

だから私の解決策は次のとおりです(上記のいくつかのアプローチに基づく):

Query sqlQuery = manager.createNativeQuery(queryStr);
List resultList = sqlQuery.getResultList();
for ( Object resultProxy : resultList ) {
    String unproxiedClob = unproxyClob(resultProxy);
    if ( unproxiedClob != null ) {
       resultCollection.add(unproxiedClob);
    }
}

private String unproxyClob(Object proxy) {
    try {
        BeanInfo beanInfo = Introspector.getBeanInfo(proxy.getClass());
        for (PropertyDescriptor property : beanInfo.getPropertyDescriptors()) {
            Method readMethod = property.getReadMethod();
            if ( readMethod.getName().contains("getWrappedClob") ) {
                Object result = readMethod.invoke(proxy);
                return clobToString((Clob) result);
            }
        }
    }
    catch (InvocationTargetException | IntrospectionException | IllegalAccessException | SQLException | IOException e) {
        LOG.error("Unable to unproxy CLOB value.", e);
    }
    return null;
}

private String clobToString(Clob data) throws SQLException, IOException {
    StringBuilder sb = new StringBuilder();
    Reader reader = data.getCharacterStream();
    BufferedReader br = new BufferedReader(reader);

    String line;
    while( null != (line = br.readLine()) ) {
        sb.append(line);
    }
    br.close();

    return sb.toString();
}

これが誰かを助けることを願っています!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.