Hazırda Bekleme proxy'sini gerçek varlık nesnesine dönüştürme


161

Hazırda Bekletme sırasında Sessionbazı nesneleri yüklüyorum ve bazıları tembel yükleme nedeniyle vekil olarak yükleniyor. Her şey yolunda ve tembel yüklemeyi kapatmak istemiyorum.

Ama daha sonra bazı nesneleri (aslında bir nesne) RPC aracılığıyla GWT istemcisine göndermem gerekiyor. Ve bu somut nesnenin bir vekil olduğu olur. Bu yüzden onu gerçek bir nesneye dönüştürmem gerekiyor. Hazırda Bekletme'de "gerçekleştir" gibi bir yöntem bulamıyorum.

Bazı nesneleri proxy'lerinden sınıflarını ve kimliklerini bilen gerçeklere nasıl dönüştürebilirim?

Şu anda gördüğüm tek çözüm, bu nesneyi Hibernate'in önbelleğinden çıkarmak ve yeniden yüklemek, ancak birçok nedenden dolayı gerçekten kötü.

Yanıtlar:


232

İşte kullandığım bir yöntem.

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
Ben de aynı şeyi yapmak istedim, bu yüzden proxy örneği bir ObjectOutputStream yazdı ve sonra karşılık gelen ObjectInputStream geri okuyun ve bu hile yapmak gibi görünüyordu. Bunun etkili bir yaklaşım olup olmadığından emin değilim, ama neden işe yaradığını merak ediyorum ... üzerine herhangi bir yorum çok takdir edilecektir. Teşekkürler!
shrini1000

@ shrini1000 çalıştı çünkü serileştirirken koleksiyonu başlatır (oturum henüz kapatılmamışsa). Ayrıca HibernateProxy, writeReplaceuygulayıcıları serileştirme sırasında özel bir şey yapmaya zorlamak için bir yöntem tanımlar .
Bozho

1
Bunu yapmanın taşınabilir (JPA) bir yolu var mı?
Kawu

Neden Hibernate.initialize lazyInitializeException'ı çağırdığımda atar? Sadece gibi kullanıyorum: Object o = session.get (MyClass.class, id); Diğer nesne = o.getSomeOtherClass (); initializeAndUnproxy (diğer);
fredcrs

6
aynısını kendi util sınıfın olmadan da yapabilirsin -(T)Hibernate.unproxy(entity)
panser

47

Bu makalede açıkladığım gibi , Hazırda Beklet ORM 5.2.10'dan beri bunu beğenebilirsiniz:

Object unproxiedEntity = Hibernate.unproxy(proxy);

Hazırda Bekletme Öncesi 5.2.10 . Bunu yapmanın en basit yolu, Hazırda Bekletme iç uygulaması tarafından sunulan unproxy yöntemini kullanmaktı PersistenceContext:

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

Bir ana varlık üzerinde bu çağrı toplama alanları işliyor mu ?? Örneğin, DepartmentListesinde bir tane varsa Student, hala ihtiyacınız var mı unproxy(department.getStudents()) - yoksa sadece yeterli unproxy(department)mi?
trafalmadorian

1
Yalnızca verilen Proxy başlatılır. Bir kök varlığın proxy'sini kaldırırsanız potansiyel olarak tonlarca veri yükleyebileceğinden, ilişkilendirmelere kademeli olarak yüklenmez.
Vlad Mihalcea

Ancak PersistentContext#unproxy(proxy)vekil ise başlatılmamış olması durumunda bir istisna atar Hibernate.unproxy(proxy)ve LazyInitializer#getImplementation(proxy)gerekirse vekil başlatın. Bu fark nedeniyle sadece bir istisna yakaladım. ;-)
bgraves

13

Kullanmaya çalışmak Hibernate.getClass(obj)


15
Bu, temelden uzaklaştırılmış nesnenin kendisinden ziyade sınıfı döndürür
Stefan Haberl

Aslında bu çözüm karşılaştırmalar için obj sınıfını bulmaya çalışırken harikadır.
João Rebelo

13

Proxy'lerden nesneyi temizleyen aşağıdaki kodu yazdım (zaten başlatılmamışlarsa)

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());
    }

}

Ben RPC hizmetleri (yönleri aracılığıyla) sonucu bu işlevi kullanın ve yinelenen tüm sonuç nesnelerini proxy'lerden (başlatılmamışsa) temizler.


tüm kullanım
senaryolarını

Doğru. Yeni vakalara göre güncellenmelidir. GWT çocuklar tarafından önerilen şeyleri deneyebilirsiniz. Buraya bakın: gwtproject.org/articles/using_gwt_with_hibernate.html (bkz. Entegrasyon Stratejileri bölümü). Genel olarak DTO veya Dozer veya Gilead kullanmanızı önerirler. Bu konuda görüşünüzü bildirirseniz iyi olur. Benim durumumda benim kod basit bir çözüm gibi görünüyor, ama tam değil = (.
Sergey Bondarev

Teşekkürler. "CollectionsUtils.containsTotallyEqual (handledObjects, value)" için bir uygulama nereden edinebiliriz?
Ilan.K

public static boolean includeTotallyEqual (Koleksiyon <?> koleksiyon, Nesne değeri) {if (isEmpty (koleksiyon)) {return false; } için (Object object: collection) {if (object == değer) {return true; } dönüş yanlış; }
Sergey Bondarev

Bu sadece kendi başıma oluşturduğum fayda yöntemi
Sergey Bondarev

10

JPA 2 ile öneriyorum:

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

2
Cevabınız benden farklı mı?
Vlad Mihalcea

Bu çözümü denedim ... unwrap komutundan önce böyle bir şey koymazsanız her zaman çalışmaz: HibernateProxy hibernateProxy = (HibernateProxy) possibleProxyObject; if (hibernateProxy.getHibernateLazyInitializer (). isUninitialized ()) {hibernateProxy.getHibernateLazyInitializer (). initialize (); }
user3227576

2

Spring Data JPA ve Hibernate ile, JpaRepository"join" stratejisi kullanılarak eşlenen bir tür hiyerarşisine ait nesneleri aramak için alt arabirimlerini kullanıyordum. Ne yazık ki, sorgular beklenen beton türlerinin örnekleri yerine taban türünün proxy'lerini döndürüyordu. Bu, sonuçları doğru türlere dökmemi engelledi. Senin gibi ben de buraya entitlerimın proxy'sini kurmanın etkili bir yolunu aradım.

Vlad, bu sonuçların proxy'sini kaldırmak için doğru fikre sahip; Yannis biraz daha ayrıntı verir. Cevaplarına ek olarak, aradığınız şeylerin geri kalanı:

Aşağıdaki kod proxy varlıklarınızın proxy'sini kaldırmak için kolay bir yol sağlar:

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;
    }

}

Proksisyonsuz entitleri veya proksiye objeleri metoda aktarabilirsiniz unproxy. Zaten proxy uygulanmazlarsa, yalnızca iade edilirler. Aksi takdirde, vekalet edilmeyecek ve geri döneceklerdir.

Bu yardımcı olur umarım!


1

Diğer bir geçici çözüm,

Hibernate.initialize(extractedObject.getSubojbectToUnproxy());

Oturumu kapatmadan hemen önce.


1

Standart Java ve JPA API kullanarak sınıfını kaldırmak için bir çözüm buldum. Hazırda bekletme ile test edilmiştir, ancak bağımlılık olarak hazırda bekletme gerektirmez ve tüm JPA sağlayıcılarıyla çalışmalıdır.

Tek bir gereksinim - ana sınıfı (Adres) değiştirmek ve basit bir yardımcı yöntem eklemek gerekir.

Genel fikir: Üst sınıfa kendini döndüren yardımcı yöntem ekleyin. yöntem proxy'de çağrıldığında, çağrıyı gerçek örneğe yönlendirir ve bu gerçek örneği döndürür.

Hazırda bekletme, proxy sınıfın kendisini döndürdüğünü ve yine de gerçek örnek yerine proxy döndürdüğünü fark ettiğinden, uygulama biraz daha karmaşıktır. Geçici çözüm, döndürülen örneği, gerçek örnekten farklı sınıf türüne sahip basit bir sarmalayıcı sınıfına sarmaktır.

Kodda:

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

class AddressWrapper {
    private Address wrappedAddress;
...
}

Adres proxy'sini gerçek alt sınıfa aktarmak için aşağıdakileri kullanın:

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

Örnek kodunuz biraz belirsiz görünüyor (ya da belki daha fazla kahveye ihtiyacım var). EntityWrapper nereden geliyor? bu AddressWrapper olmalı mı? Ve sanırım AddressWrapped AddressWrapper? Bunu açıklığa kavuşturabilir misiniz?
Gus

@Gus, haklısın. Örneği düzelttim. Teşekkürler :)
OndroMih


0

Önerilen çözümler için teşekkür ederiz! Ne yazık ki, hiçbiri benim durumum için işe yaramadı: yerel bir sorgu kullanarak, Oracle veritabanından JPA - Hibernate aracılığıyla CLOB nesnelerinin bir listesini alma.

Önerilen tüm yaklaşımlar bana bir ClassCastException veya sadece döndürdü java Proxy nesnesi verdi (ki içinde derinden istenen Clob içeriyordu).

Benim çözümüm şudur (yukarıdaki yaklaşımlara dayanarak):

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();
}

Umarım bu birine yardımcı olur!

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.