如何将 Hibernate 代理转换为实体对象

在 Hibernate Session期间,我加载了一些对象,其中一些由于延迟加载而被作为代理加载。一切都很好,我不想把懒惰的负载关掉。

但是稍后我需要通过 RPC 将一些对象(实际上是一个对象)发送到 GWT 客户机。碰巧这个具体的对象是一个代理。所以我需要把它变成一个真实的物体。我在 Hibernate 中找不到“具体化”这样的方法。

我如何才能把一些对象从代理转换为实数,知道它们的类和 ID?

目前,我看到的唯一解决方案是从 Hibernate 的缓存中删除该对象并重新加载它,但由于许多原因,这种方法真的很糟糕。

114762 次浏览

这是我正在使用的一种方法。

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

尝试使用 Hibernate.getClass(obj)

我找到了一个解决方案,可以使用标准 Java 和 JPAAPI 来解代一个类。使用 hibernate 进行测试,但不需要将 hibernate 作为一个依赖项,应该可以与所有 JPA 提供程序一起使用。

只有一个要求-它必须修改父类(Address)并添加一个简单的辅助方法。

总体思路: 将 helper 方法添加到返回自身的父类中。方法调用代理时,它会将调用转发给实例并返回这个实例。

实现稍微复杂一些,因为 Hibernate 认识到代理类返回自身,并且仍然返回代理而不是实际实例。解决方案是将返回的实例封装到一个简单的包装类中,该类与实际实例具有不同的类类型。

代码:

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


class AddressWrapper {
private Address wrappedAddress;
...
}

要将 Address 代理转换为实际子类,请使用以下命令:

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

我写了下面的代码来清除代理中的对象(如果它们还没有初始化的话)

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 服务的结果上使用这个函数(通过方面) ,它递归地清除代理中的所有结果对象(如果它们没有初始化的话)。

因为 Hibernate ORM 5.2.10,你可以这样做:

Object unproxiedEntity = Hibernate.unproxy(proxy);

休眠前 5.2.10。最简单的方法是使用 Hibernate 内部 PersistenceContext实现提供的 非代理人方法:

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

我推荐 JPA 2的方式:

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

另一个变通方法是调用

Hibernate.initialize(extractedObject.getSubojbectToUnproxy());

就在会议结束之前。

谢谢你提出的解决方案!不幸的是,它们都不适合我的情况: 使用本机查询,通过 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();
}

希望这对谁有帮助!

使用 Spring Data JPA 和 Hibernate,我使用 JpaRepository的子接口查找属于使用“ join”策略映射的类型层次结构的对象。不幸的是,查询返回的是基类型的代理,而不是预期的具体类型的实例。这使我无法将结果转换为正确的类型。像你一样,我来这里寻找一个有效的方法来让我的实体不被代理。

Vlad 提出了取消代理这些结果的正确想法; Yannis 提供了更多的细节。除了他们的回答,以下是你可能会寻找的其他答案:

下面的代码提供了一种简单的方法来取消代理您的代理实体:

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方法。如果它们已经没有代理,那么只需返回它们。否则,他们会得到无代理和返回。

希望这个能帮上忙!

Hiebrnate 5.2.10开始,您可以使用 Hibernate 代理方法将代理转换为您的实体:

MyEntity myEntity = (MyEntity) Hibernate.unproxy( proxyMyEntity );