59

サーバーから次のエラー応答が表示されます。

HTTPステータス500-

タイプ例外レポート

メッセージ

descriptionサーバーで内部エラー()が発生したため、この要求を実行できませんでした。

例外

javax.servlet.ServletException:java.lang.UnsupportedOperationException:java.lang.Classをシリアル化しようとしました:org.hibernate.proxy.HibernateProxy。タイプアダプタの登録を忘れましたか?

根本的な原因

java.lang.UnsupportedOperationException:java.lang.Classをシリアル化しようとしました:org.hibernate.proxy.HibernateProxy。タイプアダプタの登録を忘れましたか?

Javaデバッガーから:

org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer@7632012e

Gsonを使用してJavaオブジェクトをJSONに変換しています。以下に、コードの一部を貼り付けました。

これは私のリソースです:

@Stateless
@LocalBean
@Path("/autos")
@Produces(MediaType.APPLICATION_JSON)
public class AutoResource {

    @EJB
    private CarAssembler warehouse;
    @Context
    private UriInfo uriInfo;

    @GET
    public Response allAutos() {
        // Building a context, lots of code...
        // Creating a Gson instance and configures it...

        final Auto auto = warehouse.list(context);
        final String autoJson = gson.toJson(auto);

        return Response.ok(autoJson).build();
    }
}

CarAssemblerは、リポジトリを呼び出す単なるサービスです。ここにサービスのコードを貼り付けていません。

リポジトリ:

@Override
public Question findById(final int id, final FetchType fetchType) {

    final Auto question = getEntityManager().find(Auto.class, id);

    if (fetchType == FetchType.LAZY) {
        return auto;
    }

    Hibernate.initialize(auto.getManufacturer());
    Hibernate.initialize(auto.getAssemblyHouse());

    return auto;
}

ご覧のとおり、オブジェクトの遅延読み込みと熱心な読み込みの両方を提供しています。Hibernate.initializeを使用して、JPAアソシエーションを熱心にフェッチします。ただし、問題は、発生しているプロキシエラーをどのように修正できるかです。なぜ、まだJavaAssistに接続されているAssemblyHouseだけがあり、Manufacturerは接続されていないのですか(Java Debuggerでタイプを見たことがあります)。オブジェクトのプロキシを解除するタイミングを知るにはどうすればよいですか?この自動が持つ可能性のあるすべての関連付けのプロキシを解除する必要がありますか?そして、私のコードのどの層にありますか?プロキシを解除すると、アプリケーションのパフォーマンスに影響しますか?他の解決策はありますか?エラーメッセージから、タイプアダプタを作成できることがわかります。はい、できますが、変換が正しく行われるように、すべてのドメインオブジェクトに対してこれを行う必要があります。ドメイン内の他のオブジェクトもJSON表現に変換しようとすると失敗し始めるかもしれませんが、いつ、なぜかはわかりません。

これは私がオブジェクトのプロキシを解除する方法ですが、これが良いか悪いか、これを行うレイヤーとタイミングがわからないため、まだ実装していません。オブジェクトのプロキシを常に解除する必要がありますか?

public class HibernateUtilities {

    public static <T> T unproxy(T proxy) {
        if (proxy == null) {
            return null;
        }

        if (proxy instanceof HibernateProxy) {
            Hibernate.initialize(proxy);

            HibernateProxy hibernateProxy = (HibernateProxy) proxy;
            T unproxiedObject = (T) hibernateProxy.getHibernateLazyInitializer().getImplementation();

            return unproxiedObject;
        }

        return proxy;
    }
}

要求に応じたスタックトレース:

[#| 2012-11-22T17:17:13.285 +0100|警告|glassfish3.1.2| javax.enterprise.system.container.web.com.sun.enterprise.web | _ThreadID = 71; _ThreadName = Thread-8; | StandardWrapperValve [javax.ws.rs.core.Application]:
PWC1406:サーブレットjavax.ws.rs.core.Applicationのサーブレット.service()
例外java.lang.UnsupportedOperationExceptionをスローしました:しようとしました
java.lang.Classをシリアル化します:org.hibernate.proxy.HibernateProxy。忘れた
タイプアダプタを登録するには?
    com.google.gson.internal.bind.TypeAdapters $ 1.write(TypeAdapters.java:64)で
    com.google.gson.internal.bind.TypeAdapters $ 1.write(TypeAdapters.java:61)で
    com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)で
    com.google.gson.internal.bind.ArrayTypeAdapter.write(ArrayTypeAdapter.java:93)で
    com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)で
    com.google.gson.internal.bind.ReflectiveTypeAdapterFactory $ 1.write(ReflectiveTypeAdapterFactory.java:89)で
    com.google.gson.internal.bind.ReflectiveTypeAdapterFactory $ Adapter.write(ReflectiveTypeAdapterFactory.java:195)で
    com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)で
    com.google.gson.internal.bind.ReflectiveTypeAdapterFactory $ 1.write(ReflectiveTypeAdapterFactory.java:89)で
    com.google.gson.internal.bind.ReflectiveTypeAdapterFactory $ Adapter.write(ReflectiveTypeAdapterFactory.java:195)で
    com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)で
    com.google.gson.internal.bind.ReflectiveTypeAdapterFactory $ 1.write(ReflectiveTypeAdapterFactory.java:89)で
    com.google.gson.internal.bind.ReflectiveTypeAdapterFactory $ Adapter.write(ReflectiveTypeAdapterFactory.java:195)で
    com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)で
    com.google.gson.internal.bind.CollectionTypeAdapterFactory $ Adapter.write(CollectionTypeAdapterFactory.java:96)で
    com.google.gson.internal.bind.CollectionTypeAdapterFactory $ Adapter.write(CollectionTypeAdapterFactory.java:60)で
    com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)で
    com.google.gson.internal.bind.ReflectiveTypeAdapterFactory $ 1.write(ReflectiveTypeAdapterFactory.java:89)で
    com.google.gson.internal.bind.ReflectiveTypeAdapterFactory $ Adapter.write(ReflectiveTypeAdapterFactory.java:195)で
    com.google.gson.Gson.toJson(Gson.java:586)で
    com.google.gson.Gson.toJson(Gson.java:565)で
    com.google.gson.Gson.toJson(Gson.java:520)で
    com.myapp.AutoResource.produceAuto(AutoResource.java:48)で
    sun.reflect.NativeMethodAccessorImpl.invoke0(ネイティブメソッド)で
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)で
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)で
    java.lang.reflect.Method.invoke(Method.java:601)で
    org.glassfish.ejb.security.application.EJBSecurityManager.runMethod(EJBSecurityManager.java:1052)で
    org.glassfish.ejb.security.application.EJBSecurityManager.invoke(EJBSecurityManager.java:1124)で
    com.sun.ejb.containers.BaseContainer.invokeBeanMethod(BaseContainer.java:5388)で
    com.sun.ejb.EjbInvocation.invokeBeanMethod(EjbInvocation.java:619)で
    com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:800)で
    com.sun.ejb.EjbInvocation.proceed(EjbInvocation.java:571)で
    com.sun.ejb.containers.interceptors.SystemInterceptorProxy.doAround(SystemInterceptorProxy.java:162)で
    com.sun.ejb.containers.interceptors.SystemInterceptorProxy.aroundInvoke(SystemInterceptorProxy.java:144)で
    sun.reflect.NativeMethodAccessorImpl.invoke0(ネイティブメソッド)で
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)で
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)で
    java.lang.reflect.Method.invoke(Method.java:601)で
    com.sun.ejb.containers.interceptors.AroundInvokeInterceptor.intercept(InterceptorManager.java:861)で
    com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:800)で
    com.sun.ejb.containers.interceptors.InterceptorManager.intercept(InterceptorManager.java:370)で
    com.sun.ejb.containers.BaseContainer .__ intercept(BaseContainer.java:5360)で
    com.sun.ejb.containers.BaseContainer.intercept(BaseContainer.java:5348)で
    com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:214)で
    com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvocationHandlerDelegate.java:89)で
    sun.reflect.NativeMethodAccessorImpl.invoke0(ネイティブメソッド)で
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)で
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)で
    java.lang.reflect.Method.invoke(Method.java:601)で
    com.sun.jersey.spi.container.JavaMethodInvokerFactory $ 1.invoke(JavaMethodInvokerFactory.java:60)で
    com.sun.jersey.server.impl.model.method.dispatch.Abs​​tractResourceMethodDispatchProvider $ ResponseOutInvoker._dispatch(AbstractResourceMethodDispatchProvider.java:205)で
    com.sun.jersey.server.impl.model.method.dispatch.ResourceJavaMethodDispatcher.dispatch(ResourceJavaMethodDispatcher.java:75)で
    com.sun.jersey.server.impl.uri.rules.HttpMethodRule.accept(HttpMethodRule.java:288)で
    com.sun.jersey.server.impl.uri.rules.ResourceClassRule.accept(ResourceClassRule.java:108)で
    com.sun.jersey.server.impl.uri.rules.RightHandPathRule.accept(RightHandPathRule.java:147)で
    com.sun.jersey.server.impl.uri.rules.RootResourceClassesRule.accept(RootResourceClassesRule.java:84)で
    com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1469)で
    com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1400)で
    com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1349)で
    com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1339)で
    com.sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.java:416)で
    com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:537)で
    com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:708)で
    javax.servlet.http.HttpServlet.service(HttpServlet.java:770)で
    org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1550)で
    org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:281)で
    org.apache.catalina.core.StandardContextValve .__ invoke(StandardContextValve.java:175)で
    org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java)で
    org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:655)で
    org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:595)で
    org.apache.catalina.core.StandardHostValve .__ invoke(StandardHostValve.java:161)で
    org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java)で
    org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:331)で
    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:231)で
    com.sun.enterprise.v3.services.impl.ContainerMapper $ AdapterCallable.call(ContainerMapper.java:317)で
    com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:195)で
    com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:860)で
    com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:757)で
    com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1056)で
    com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:229)で
    com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)で
    com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)で
    com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)で
    com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)で
    com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)で
    com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)で
    com.sun.grizzly.ContextTask.run(ContextTask.java:71)で
    com.sun.grizzly.util.AbstractThreadPool $ Worker.doWork(AbstractThreadPool.java:532)で
    com.sun.grizzly.util.AbstractThreadPool $ Worker.run(AbstractThreadPool.java:513)で
    java.lang.Thread.run(Thread.java:722)で|#]
4

6 に答える 6

95

カスタムを使用すると、すべてを手動でプロキシ解除せずに実行できますTypeAdapter。これらの線に沿った何か:

/**
 * This TypeAdapter unproxies Hibernate proxied objects, and serializes them
 * through the registered (or default) TypeAdapter of the base class.
 */
public class HibernateProxyTypeAdapter extends TypeAdapter<HibernateProxy> {

    public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
        @Override
        @SuppressWarnings("unchecked")
        public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
            return (HibernateProxy.class.isAssignableFrom(type.getRawType()) ? (TypeAdapter<T>) new HibernateProxyTypeAdapter(gson) : null);
        }
    };
    private final Gson context;

    private HibernateProxyTypeAdapter(Gson context) {
        this.context = context;
    }

    @Override
    public HibernateProxy read(JsonReader in) throws IOException {
        throw new UnsupportedOperationException("Not supported");
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    @Override
    public void write(JsonWriter out, HibernateProxy value) throws IOException {
        if (value == null) {
            out.nullValue();
            return;
        }
        // Retrieve the original (not proxy) class
        Class<?> baseType = Hibernate.getClass(value);
        // Get the TypeAdapter of the original class, to delegate the serialization
        TypeAdapter delegate = context.getAdapter(TypeToken.get(baseType));
        // Get a filled instance of the original class
        Object unproxiedValue = ((HibernateProxy) value).getHibernateLazyInitializer()
                .getImplementation();
        // Serialize the value
        delegate.write(out, unproxiedValue);
    }
}

使用するには、最初に登録する必要があります。

GsonBuilder b = new GsonBuilder();
...
b.registerTypeAdapterFactory(HibernateProxyTypeAdapter.FACTORY);
...
Gson gson = b.create();

これにより、オブジェクト階層にあるすべてのプロキシが再帰的に初期化されることに注意してください。ただし、データ全体をシリアル化する必要があるため、とにかくそれを実行する必要があります。

これはどのように作動しますか?

GSONには、TypeAdapterFactoryさまざまなタイプ(プリミティブ型、Stringまたはなどの一般的な型Date、リスト、配列など)用の多数の実装が含まれています。各ファクトリは、特定のJavaタイプをシリアル化できるかどうかを尋ねられます(にないジェネリック型に関する可能な情報をキャプチャするために、パラメータはでcreateTypeTokenなくになります)。ファクトリが型をシリアル化/逆シリアル化できる場合、インスタンスで応答します。それ以外の場合は、で応答します。ClassClassTypeAdapternull

HibernateProxyTypeAdapter.FACTORYタイプが実装されているかどうかを確認しHibernateProxyます; その場合、HibernateProxyTypeAdapterシリアル化のためにのインスタンスを返します。このwriteメソッドは、実際のオブジェクトをシリアル化する必要があるときに呼び出されます。アダプタは、基になるオブジェクトの元のタイプを抽出し、GSONにTypeAdapter元のタイプの標準(通常は。)を要求しReflectiveTypeAdapterます。

次に、プロキシを直接使用する代わりに、元のクラスのインスタンスを取得します。これが必要なのは、ゲッターを使用する代わりに、フィールドに直接ReflectiveTypeAdapterアクセスするためです。プロキシされたオブジェクトのフィールドへのアクセスは機能せず、古典的なHibernateの落とし穴です。

パフォーマンスを向上させるために、メソッドでデリゲートTypeAdapterを取得する必要があります。プロキシcreateを呼び出すと、元の基本クラスが生成されるように見えることがわかりました。コードは次のようになります。getSuperclass()Class

public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
    @Override
    @SuppressWarnings("unchecked")
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        return (HibernateProxy.class.isAssignableFrom(type.getRawType())
                ? (TypeAdapter<T>) new HibernateProxyTypeAdapter((TypeAdapter)gson.getAdapter(TypeToken.get(type.getRawType().getSuperclass()))) 
     : null);
    }
};
private final TypeAdapter<Object> delegate;

private HibernateProxyTypeAdapter(TypeAdapter<Object> delegate) {
    this.delegate = delegate;
}

@SuppressWarnings({"rawtypes", "unchecked"})
@Override
public void write(JsonWriter out, HibernateProxy value) throws IOException {
    if (value == null) {
        out.nullValue();
        return;
    }
    delegate.write(out, ((HibernateProxy) value).getHibernateLazyInitializer()
            .getImplementation());
}
于 2012-11-23T08:44:50.353 に答える
4

エラーが積極的な読み込みで持続すると述べたように見える場合、問題はおそらくHibernateではなく、GSONの実装にある可能性があります。JSONを作成するときにタイプが必要になると思います。登録されているかどうかはわかりませんが、おそらく次のようなものです。

public String autosToJson(Auto autos) {  
    GsonBuilder gsonBuilder = new GsonBuilder();
    Gson gson = gsonBuilder.registerTypeAdapter(Auto.class, new AutoAdapter()).create();
    return gson.toJson(autos);
}   

次に、AdapterClassを作成します。次に例を示します。

public class AutoAdapter implements JsonSerializer<Auto> {
  @Override
  public JsonElement serialize(Auto auto, Type type, JsonSerializationContext jsc) {
    JsonObject jsonObject = new JsonObject();
    jsonObject.addProperty("auto_id", auto.getId());
    jsonObject.addProperty("auto_name", auto.getAutoName());
    jsonObject.addProperty("auto__manufacture_date", auto.getManufactureDate().toString());
    return jsonObject;      
  }
}
于 2012-11-22T13:51:54.913 に答える
2

通常、ドメインオブジェクトをサービスを介してXML / JSONとして公開したくない場合は、エンティティがコンシューマーのニーズに合わないため、DTOを作成する必要があります。そして、今はそうだとしても、データベースの内部リファクタリングの後、明日は適合しません。したがって、このような問題が発生した場合は、今すぐDTOを作成することをお勧めします。ところで、結果トランスフォーマーを使用するか、ビューを作成してHibernateエンティティをこれらのビューにマッピングすることで、HibernateレベルでもこれらのDTOを作成できます。

もう1つのトリックは、必要なフィールドを他のクラス(実際には同じクラスですが、プロキシなし)にコピーするためにDozerを使用することです。

注:アクセサーの代わりにフィールドにアクセスするGsonを使用している場合、プロキシ自体のフィールドに常にアクセスしようとするため、Hibernateプロキシを使用できなくなりますnull

于 2012-11-22T07:02:49.927 に答える
1

はい、いつでもプロキシを解除できます。HibernateProxy(シリアル化されない)がある場合は、削除されて実際の基盤となる実装に置き換えられるか、クラスをそのままにして実装を提供します。私はあなたの解決策がうまくいくはずだと思います。私はHibernateをあまり使用していませんが、それは私には理にかなっています。

一方、Hibernateをもう少し信頼しているかもしれませんが、より簡単な方法は次のようになります。

Hibernate.getClass(obj);

このソリューションでは、実装/初期化されたクラスを提供するのではなく、クラスのみを提供するか、その関数を次の方法で提供する必要があります。

HibernateProxyHelper.getClassWithoutInitializingProxy(superClass)

ただし、後者はスーパークラスを返す可能性があると思うので、Hibernate.getClass(obj);から始めることができます。

また:

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プロキシを実際のオブジェクトに変換する場合、エンティティが常にプロキシであることを意味するわけではないため、変数名の方がおそらく優れています。また、警告するために例外をスローしますが、例外が必要かどうかはあなた次第です。

もちろん、遅延読み込みを取り除くこともできますが、それが最善の解決策ではないと思います。

于 2012-11-21T20:25:30.990 に答える
1

ObjectMapperを次のように解析してみてください

final Auto auto = warehouse.list(context);
final String autoJson = new ObjectMapper().writeValueAsString(auto);

return Response.ok(autoJson).build();
于 2020-10-15T07:35:57.453 に答える
0

私がこの投稿にぶつかったとき、私はこの非常に問題を経験していました。それは私の状況に正しい方向を示しました。特に、一部のフィールドを遅延読み込みとしてマークしたという理由から、エンティティ全体をシリアル化する必要がないことに気付きました。だから私はこれらのフィールドをスキップする方法を見つけようとしました、そしてExclusionStrategyは魔法でした。これで問題が解決したようです

public class ExcludeProxiedFields implements ExclusionStrategy{

    @Override
    public boolean shouldSkipField(FieldAttributes fa) {
        return fa.getAnnotation(ManyToOne.class) != null ||
           fa.getAnnotation(OneToOne.class) != null  ||
           fa.getAnnotation(ManyToMany.class) != null  ||
           fa.getAnnotation(OneToMany.class) != null ;
    }

    @Override
    public boolean shouldSkipClass(Class<?> type) {
        return false;
    }   
}

次に、このクラスを次のようにGsonBuilderに適用しました。

Gson gson = new GsonBuilder().setExclusionStrategies(new ExcludeProxiedFields()).create();
于 2017-10-23T03:49:45.613 に答える