Hibernate Session
の実行中に、いくつかのオブジェクトを読み込んでいますが、それらのいくつかは遅延読み込みのためにプロキシとして読み込まれています。それはすべて大丈夫で、私は遅延読み込みをオフにしたくありません。
しかし、後でRPCを介していくつかのオブジェクト(実際には1つのオブジェクト)をGWTクライアントに送信する必要があります。そして、この具象オブジェクトはプロキシであることがあります。だから私はそれを実際のオブジェクトに変える必要があります。 Hibernateに「実体化」のようなメソッドが見つかりません。
オブジェクトの一部をプロキシからクラスとIDを知っている実数に変換するにはどうすればよいですか?
現時点で唯一の解決策は、そのオブジェクトをHibernateのキャッシュから削除して再ロードすることですが、それは多くの理由で本当に悪いです。
ここに私が使用している方法があります。
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;
}
プロキシからオブジェクトを削除する次のコードを書きました(まだ初期化されていない場合)
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.getClass(obj)
を使用してみてください
JPA 2で推奨する方法:
Object unproxied = entityManager.unwrap(SessionImplementor.class).getPersistenceContext().unproxy(proxy);
Spring Data JPAとHibernateでは、JpaRepository
のサブインターフェースを使用して、「結合」戦略を使用してマップされた型階層に属するオブジェクトを検索していました。残念ながら、クエリは、予想される具象型のインスタンスではなく、ベース型のプロキシを返していました。これにより、結果を正しいタイプにキャストできませんでした。あなたのように、私はここに来て、私のエンティティをプロキシ化しない効果的な方法を探しました。
Vladはこれらの結果のプロキシを解除するための正しい考えを持っています。ヤニスはもう少し詳細を提供します。彼らの答えに加えて、あなたが探しているかもしれないものの残りはここにあります:
次のコードは、プロキシされたエンティティのプロキシを解除する簡単な方法を提供します。
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
メソッドに渡すことができます。既にプロキシ化されていない場合は、単に返されます。そうしないと、プロキシされずに返されます。
お役に立てれば!
別の回避策は、呼び出すことです
Hibernate.initialize(extractedObject.getSubojbectToUnproxy());
セッションを閉じる直前。
標準の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;
}
提案された解決策をありがとう!残念ながら、私の場合はそれらのいずれも機能しませんでした。ネイティブクエリを使用して、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();
}
これが誰かを助けることを願っています!
Hiebrnate 5.2.1 から開始して、 Hibernate.proxy メソッドを使用して、プロキシを実際のエンティティに変換できます。
MyEntity myEntity = (MyEntity) Hibernate.unproxy( proxyMyEntity );