Error building JSON of an Object recovered by Hibernate

Asked

Viewed 2,352 times

2

Error building Json of an Object recovered by Hibernate.

session.getTransaction().begin();        
ArrayList<Cliente> lista = new ArrayList<>(session.createCriteria(Cliente.class).list());
String json = new Gson().toJson(lista); // o erro acontece aqui
session.close();

return json;

Error log

java.lang.UnsupportedOperationException: Attempted to serialize java.lang.Class: org.hibernate.proxy.HibernateProxy. Forgot to register a type adapter?
    com.google.gson.internal.bind.TypeAdapters$1.write(TypeAdapters.java:67)
    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.ObjectTypeAdapter.write(ObjectTypeAdapter.java:107)
    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.Gson.toJson(Gson.java:593)
    com.google.gson.Gson.toJson(Gson.java:572)
    com.google.gson.Gson.toJson(Gson.java:527)
    com.google.gson.Gson.toJson(Gson.java:507)
    com.embrapa.pragasdocampo.resource.PragaResource.getLista(PragaResource.java:32)
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    java.lang.reflect.Method.invoke(Method.java:483)
    com.sun.jersey.spi.container.JavaMethodInvokerFactory$1.invoke(JavaMethodInvokerFactory.java:60)
    com.sun.jersey.server.impl.model.method.dispatch.AbstractResourceMethodDispatchProvider$TypeOutInvoker._dispatch(AbstractResourceMethodDispatchProvider.java:185)
    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.RightHandPathRule.accept(RightHandPathRule.java:147)
    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:1465)
    com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1396)
    com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1345)
    com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1335)
    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:699)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    org.netbeans.modules.web.monitor.server.MonitorFilter.doFilter(MonitorFilter.java:393)
  • This seems like a classic error when working with json/jpa (usually the error is present where working with proxys for objects and/or 1-N/N-N relationships). Instead of returning the objects returned by Hibernate, please request new Clients, assign their attributes to the new instances, and return this new list (that way, you won’t be trying to return the client proxy list, but concrete Client objects).

  • Exactly weyel, the most practical and functional way that worked was by cloning the object.

2 answers

3

I came to the conclusion after several searches and the problem is that the Client object has as attribute another Category object. The data of the client object are in memory, already the data of the Category were not footprints of the database yet, Category is referenced with a proxy, that is, Hibernate only takes this object of the database when vc get it to not occur outdated data problem. And it is no use to create a new Object that receives another, because it is only receiving the reference, the solution that worked is to make a clone of the object. creating a @Override clone method of the Client object. example

public Cliente clone(){
        Cliente cliente = new Cliente();
        cliente.setNome(nome);
        cliente.setSobreNome(sobreNome);
        //...
        cliente.setCategoria(categoria.clone()); // dentro de categoria vc cria o mesmo método clone da mesma forma que foi feito aqui.
        return cliente;
}

Be careful not to call the method super, because if there is a relation N to N, it will have to be treated, or it will give problem.

Don’t forget to create the clone method in the Category class, this is fundamental.

public Categoria clone(){
    Categoria categoria = new Categoria();
    categoria.setId(id)
    categoria.setNome(nome);
    return categoria;
}

In my case Hibernate is returning a list of Clients, so just implement it in the best way you can find a method that clones all clients for a new Arraylist.

Regarding the processing cost, I’m not the best to answer, but so far it’s the only solution I’ve found best.

Update 29 Nov 2014

I’ve been studying a little more about JPA and decided to add one more little information in this post.

By default from JPA:

• Every time a relationship ends on One ( Onetoone , Manytoone ) it will be by default EAGER.

• Every time a relationship ends in Many ( Onetomany , Manytomany ) it will be by default LAZY.

EAGER means that when Fetching a Client Object that has as attribute Category(Other object), in the act of Select Client in the database data data, category object will be loaded in memory.

LAZY signigica that when Fetching a Client Object that has as attribute Category(Other object), in the act of Select Client in the database data, category object will not be loaded in memory only on when the.getCategory() client method is invoked.

This is a JPA standard, but can be changed, and possibly the Hibernate is doing this.

It may have nothing to do with my problem, but I found it interesting share this concept.

  • Both classes inherit characteristics of Serializable? If I’m not mistaken, Hibernate needs his models to be Serializable.

  • Yes all my entities implemented Serializable, Hibernate itself automatically mapped the tables of my database creating my entities. Perhaps the best solution could be this http://stackoverflow.com/questions/13459718/could-not-serialize-object-cause-of-hibernateproxy but I couldn’t get it to work

  • Although your initial problem is not yet this one you quoted (lazyInit/proxy) rs, you would probably bump into it soon after solving the proxys problem. To understand what is proxy, take a look at this link -> https://docs.oracle.com/javase/6/docs/api/java/lang/reflect/Proxy.html (and as the exception said, proxys from Hibernate are not serializable) []’s

2


I come to add another solution to this question, I went through the same problem recently and researching found out about serialization using the @Expose annotation on entities and about the mapping of manual Json.

Using the annotation @Expose and the instantiation Gsonbuilder we can indicate which attribute will be serialized in our class. In the example below all the fields that have the @Expose annotation will be serialized, the rest will be ignored. (Let’s hide getters and setters in the examples).

Instantiating Gsonbuilder:

Gson gson = new GsonBuilder()
  .excludeFieldsWithoutExposeAnnotation() //ignorar campos sem a anotação @Expose
  .setPrettyPrinting() //formata o json para imprimir no console de forma mais legível 
  .create();
String jsonString = gson.toJson(Casa);

Class with a subclass:

public class Casa {  
    @Expose  
    private long numeroEndereco;  
    @Expose   
    private String nomeRua;  
    private String nomeBairro; 
    @Expose    
    private Movel movel;  
}  


public class Movel {  
    @Expose   
    private long numeroSerie;  
    @Expose   
    private String descricao;  
    private String cor;  
}

Example console output using println:

{
   "numeroEndereco": "1",
   "nomeRua": "AvenidaSaoJoao",
   "movel": {
       "numeroSerie": 1,
       "descricao": "qualquer"
   },
} 

In that case the attributes would be ignored nomeBairro and color that do not have the rating @Expose and with that notation would indicate to GSON that we want to serialize the object mobile within the class House.

If you want to map the subclass attribute directly to the json we have the option to do the manual insertion. We will use the same class, subclass and Gsonbuilder from the previous example but removing the @Expose of the subclass, we have the following:

public class Casa{  
    @Expose  
    private long numeroEndereco;  
    @Expose   
    private String nomeRua;  
    private String nomeBairro;     
    private Movel movel;  
}  

JsonElement jsonElement = gson.toJsonTree(Casa); //Serializa a classe Casa, como utilizamos o msm builder os atributos sem a notação @Expose vão ser ignorados
      jsonElement.getAsJsonObject().addProperty("corDoMovel", movel.getCor()); // Atribuindo a key "corDoMovel"  e o value getCor() presente na subclasse movel
      jsonStr = gson.toJson(jsonElement); // Serializa o json com o novo atributo

Example console output using println:

{
  "numeroEndereco": "1",
  "nomeRua": "AvenidaSaoJoao",
  "corDoMovel": "Branco"
} 

In this example we serialize the class House ignoring the object mobile present in the class and later capture the color attribute and insert in our json that has been serialized.

I hope that with this brief example I can help you in some way :). If you know more optimized ways to solve this problem add to the post or correct this example.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.