Java 如何将 Hibernate 代理转换为真实的实体对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2216547/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to convert a Hibernate proxy to a real entity object
提问by Andrey Minogin
During a Hibernate Session
, I am loading some objects and some of them are loaded as proxies due to lazy loading. It's all OK and I don't want to turn lazy loading off.
在 Hibernate 期间Session
,我正在加载一些对象,其中一些由于延迟加载而作为代理加载。一切正常,我不想关闭延迟加载。
But later I need to send some of the objects (actually one object) to the GWT client via RPC. And it happens that this concrete object is a proxy. So I need to turn it into a real object. I can't find a method like "materialize" in Hibernate.
但是稍后我需要通过 RPC 将一些对象(实际上是一个对象)发送到 GWT 客户端。碰巧这个具体对象是一个代理。所以我需要把它变成一个真实的对象。我在 Hibernate 中找不到像“实现”这样的方法。
How can I turn some of the objects from proxies to reals knowing their class and ID?
如何将某些对象从代理转换为知道它们的类和 ID 的实数?
At the moment the only solution I see is to evict that object from Hibernate's cache and reload it, but it is really bad for many reasons.
目前我看到的唯一解决方案是从 Hibernate 的缓存中驱逐该对象并重新加载它,但由于多种原因,这确实很糟糕。
采纳答案by Bozho
Here's a method I'm using.
这是我正在使用的一种方法。
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;
}
回答by Sanek Shu
Try to use Hibernate.getClass(obj)
尝试使用 Hibernate.getClass(obj)
回答by OndroMih
I found a solution to deproxy a class using standard Java and JPA API. Tested with hibernate, but does not require hibernate as a dependency and should work with all JPA providers.
我找到了一种使用标准 Java 和 JPA API 对类进行代理的解决方案。使用 hibernate 进行了测试,但不需要 hibernate 作为依赖项,并且应该与所有 JPA 提供程序一起使用。
Onle one requirement - its necessary to modify parent class (Address) and add a simple helper method.
只有一个要求 - 有必要修改父类(地址)并添加一个简单的辅助方法。
General idea: add helper method to parent class which returns itself. when method called on proxy, it will forward the call to real instance and return this real instance.
总体思路:将辅助方法添加到返回自身的父类。当方法在代理上调用时,它将调用转发到真实实例并返回这个真实实例。
Implementation is a little bit more complex, as hibernate recognizes that proxied class returns itself and still returns proxy instead of real instance. Workaround is to wrap returned instance into a simple wrapper class, which has different class type than the real instance.
实现有点复杂,因为 hibernate 认识到代理类返回自身并且仍然返回代理而不是真实实例。解决方法是将返回的实例包装到一个简单的包装类中,该类具有与真实实例不同的类类型。
In code:
在代码中:
class Address {
public AddressWrapper getWrappedSelf() {
return new AddressWrapper(this);
}
...
}
class AddressWrapper {
private Address wrappedAddress;
...
}
To cast Address proxy to real subclass, use following:
要将地址代理转换为真正的子类,请使用以下命令:
Address address = dao.getSomeAddress(...);
Address deproxiedAddress = address.getWrappedSelf().getWrappedAddress();
if (deproxiedAddress instanceof WorkAddress) {
WorkAddress workAddress = (WorkAddress)deproxiedAddress;
}
回答by Sergey Bondarev
I've written following code which cleans object from proxies (if they are not already initialized)
我编写了以下代码来清除代理中的对象(如果它们尚未初始化)
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());
}
}
I use this function over result of my RPC services (via aspects) and it cleans recursively all result objects from proxies (if they are not initialized).
我在我的 RPC 服务的结果上使用这个函数(通过方面),它从代理中递归地清除所有结果对象(如果它们没有初始化)。
回答by Vlad Mihalcea
As I explained in this article, since Hibernate ORM 5.2.10, you can do it likee this:
正如我在这篇文章中所解释的,从 Hibernate ORM 5.2.10 开始,你可以这样做:
Object unproxiedEntity = Hibernate.unproxy(proxy);
Before Hibernate 5.2.10. the simplest way to do that was to use the unproxymethod offered by Hibernate internal PersistenceContext
implementation:
在 Hibernate 5.2.10之前。最简单的方法是使用Hibernate 内部实现提供的unproxy方法PersistenceContext
:
Object unproxiedEntity = ((SessionImplementor) session)
.getPersistenceContext()
.unproxy(proxy);
回答by Yannis JULIENNE
The way I recommend with JPA 2 :
我推荐使用 JPA 2 的方式:
Object unproxied = entityManager.unwrap(SessionImplementor.class).getPersistenceContext().unproxy(proxy);
回答by 0x6B6F77616C74
The another workaround is to call
另一种解决方法是调用
Hibernate.initialize(extractedObject.getSubojbectToUnproxy());
Just before closing the session.
就在结束会议之前。
回答by Dmitry
Thank you for the suggested solutions! Unfortunately, none of them worked for my case: receiving a list of CLOB objects from Oracle database through JPA - Hibernate, using a native query.
感谢您提供建议的解决方案!不幸的是,它们都不适用于我的情况:使用本机查询通过 JPA - Hibernate 从 Oracle 数据库接收 CLOB 对象列表。
All of the proposed approaches gave me either a ClassCastException or just returned java Proxy object (which deeply inside contained the desired Clob).
所有提议的方法都给了我一个 ClassCastException 或者只是返回了 java Proxy 对象(它在内部包含了所需的 Clob)。
So my solution is the following (based on several above approaches):
所以我的解决方案如下(基于上述几种方法):
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();
}
Hope this will help somebody!
希望这会帮助某人!
回答by Sharky
With Spring Data JPA and Hibernate, I was using subinterfaces of JpaRepository
to look up objects belonging to a type hierarchy that was mapped using the "join" strategy. Unfortunately, the queries were returning proxies of the base type instead of instances of the expected concrete types. This prevented me from casting the results to the correct types. Like you, I came here looking for an effective way to get my entites unproxied.
使用 Spring Data JPA 和 Hibernate,我使用 的子接口JpaRepository
来查找属于使用“连接”策略映射的类型层次结构的对象。不幸的是,查询返回的是基本类型的代理,而不是预期的具体类型的实例。这使我无法将结果转换为正确的类型。和你一样,我来到这里寻找一种有效的方法来让我的实体不受代理。
Vlad has the right idea for unproxying these results; Yannis provides a little more detail. Adding to their answers, here's the rest of what you might be looking for:
Vlad 对取消代理这些结果有正确的想法;Yannis 提供了更多细节。除了他们的答案之外,以下是您可能正在寻找的其他内容:
The following code provides an easy way to unproxy your proxied entities:
以下代码提供了一种取消代理代理实体的简单方法:
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;
}
}
You can pass either unproxied entites or proxied entities to the unproxy
method. If they are already unproxied, they'll simply be returned. Otherwise, they'll get unproxied and returned.
您可以将未代理的实体或代理的实体传递给该unproxy
方法。如果它们已经取消代理,它们将被简单地返回。否则,它们将被取消代理并返回。
Hope this helps!
希望这可以帮助!
回答by O.Badr
Starting from Hiebrnate 5.2.10you can use Hibernate.proxymethod to convert a proxy to your real entity:
从Hiebrnate 5.2.10开始,您可以使用Hibernate.proxy方法将代理转换为您的真实实体:
MyEntity myEntity = (MyEntity) Hibernate.unproxy( proxyMyEntity );