¿No se encontró ningún serializador para la clase org.hibernate.proxy.pojo.javassist.Javassist?


94

Estoy trabajando en SpringMVC, Hibernatey JSONpero estoy consiguiendo este error.

HTTP Status 500 - Could not write JSON: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationConfig.SerializationFeature.FAIL_ON_EMPTY_BEANS) ) 

Por favor verifique mi entidad a continuación

    @Entity
@Table(name="USERS")
public class User {

    @Id
    @GeneratedValue
    @Column(name="USER_ID")
    private Integer userId;

    @Column(name="USER_FIRST_NAME")
    private String firstName;

    @Column(name="USER_LAST_NAME")
    private String lastName;


    @Column(name="USER_MIDDLE_NAME")
    private String middleName;

    @Column(name="USER_EMAIL_ID")
    private String emailId;

    @Column(name="USER_PHONE_NO")
    private Integer phoneNo;

    @Column(name="USER_PASSWORD")
    private String password;

    @Column(name="USER_CONF_PASSWORD")
    private String  confPassword;

    @Transient
    private String token;

    @Column(name="USER_CREATED_ON")
    private Date createdOn;

    @OneToMany(fetch=FetchType.EAGER,cascade=CascadeType.ALL)
    @Fetch(value = FetchMode.SUBSELECT)
    @JoinTable(name = "USER_ROLES", joinColumns = { @JoinColumn(name = "USER_ID") }, inverseJoinColumns = { @JoinColumn(name = "ROLE_ID") })
    private List<ActifioRoles> userRole = new ArrayList<ActifioRoles>();


    @OneToMany(fetch=FetchType.EAGER,cascade=CascadeType.ALL,mappedBy="userDetails")
    @Fetch(value = FetchMode.SUBSELECT)
    private List<com.actifio.domain.Address> userAddress = new ArrayList<com.actifio.domain.Address>();

    @OneToOne(cascade=CascadeType.ALL)
    private Tenant tenantDetails;


    public Integer getUserId() {
        return userId;
    }
    public void setUserId(Integer userId) {
        this.userId = userId;
    }
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public String getEmailId() {
        return emailId;
    }
    public void setEmailId(String emailId) {
        this.emailId = emailId;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getConfPassword() {
        return confPassword;
    }
    public void setConfPassword(String confPassword) {
        this.confPassword = confPassword;
    }
    public Date getCreatedOn() {
        return createdOn;
    }
    public void setCreatedOn(Date createdOn) {
        this.createdOn = createdOn;
    }

    public List<ActifioRoles> getUserRole() {
        return userRole;
    }

    public void setUserRole(List<ActifioRoles> userRole) {
        this.userRole = userRole;
    }
    public String getMiddleName() {
        return middleName;
    }
    public void setMiddleName(String middleName) {
        this.middleName = middleName;
    }
    public Integer getPhoneNo() {
        return phoneNo;
    }
    public void setPhoneNo(Integer phoneNo) {
        this.phoneNo = phoneNo;
    }

    public List<com.actifio.domain.Address> getUserAddress() {
        return userAddress;
    }
    public void setUserAddress(List<com.actifio.domain.Address> userAddress) {
        this.userAddress = userAddress;
    }
    public Tenant getTenantDetails() {
        return tenantDetails;
    }
    public void setTenantDetails(Tenant tenantDetails) {
        this.tenantDetails = tenantDetails;
    }
    public String getToken() {
        return token;
    }
    public void setToken(String token) {
        this.token = token;
    }

    }

¿Como puedo resolver esto?


Muestre el seguimiento de pila y el código donde se produce la excepción
geo y

Sin tener ningún conocimiento de lo que intenta hacer su código, es un poco difícil de depurar, pero probablemente querrá revisar github.com/FasterXML/jackson-datatype-hibernate ya que está usando Jackson e Hibernate
geoand

¿Estás intentando crear un JSON a partir de esta clase? En este caso, el serializador JSON intenta escribir todas las propiedades, también el HashSet de sus relaciones de muchos a muchos; esto hace una excepción de inicializador perezoso
Angelo Immediata

la misma pregunta se puede encontrar en stackoverflow.com/questions/4362104/…
Matrix Buster

@ user2963481 ... Buena y muy útil pregunta Bro.
Cerebro

Respuestas:


192

Tuve un problema similar con la carga diferida a través del objeto proxy de hibernación. Lo solucioné anotando la clase que tenía propiedades privadas cargadas de forma diferida con:

@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})

Supongo que puede agregar las propiedades en su objeto proxy que rompe la serialización JSON a esa anotación.

El problema es que las entidades se cargan de forma perezosa y la serialización ocurre antes de que se carguen por completo.

Hibernate.initialize(<your getter method>);

3
También funcionó para mí ... ¿Hay alguna explicación para este comportamiento extraño?
Victor

7
@ ankur-singhal debe enfatizar que esta anotación es necesaria en la clase anidada y NO en la clase que llama.
Darwayne

1
Yahooo ... funcionó. Lo intenté varias veces y funcionó. Intenté de varias maneras y BoooM. Trabajó.
Brain

2
Gracias también funcionó para mí. Debe agregar esta anotación a las entidades que acceden a otras entidades que tienen beans de carga diferida.
Shafqat Shafi

3
Creo que esta no es una solución adecuada para el problema. Cuando serializamos, esperamos que se devuelva el subobjeto completo o al menos la clave principal del subobjeto en el resultado. Agregar estas anotaciones simplemente suprimirá el error, pero no le proporcionará los resultados deseados.
Anand Vaidya

84

Solo para agregar esto, me encontré con el mismo problema, pero las respuestas proporcionadas no funcionaron. Lo arreglé tomando la sugerencia de la excepción y agregando al archivo application.properties ...

spring.jackson.serialization.fail-on-empty-beans=false

Estoy usando Spring Boot v1.3 con Hibernate 4.3

Ahora serializa todo el objeto y los objetos anidados.

EDITAR: 2018

Dado que esto todavía recibe comentarios, lo aclararé aquí. Esto absolutamente solo oculta el error. Las implicaciones de rendimiento están ahí. En ese momento, necesitaba algo para entregar y trabajar en ello más tarde (lo que hice al dejar de usar Spring). Entonces, sí, escuche a alguien más si realmente quiere resolver el problema. Si solo quiere que desaparezca por ahora, continúe y use esta respuesta. Es una idea terrible, pero diablos, podría funcionar para ti. Para el registro, nunca volví a tener un bloqueo o problema después de esto. Pero probablemente sea la fuente de lo que terminó siendo una pesadilla de rendimiento de SQL.


8
Resuelve el problema, pero json contendrá dos propiedades adicionales innecesarias"handler":{},"hibernateLazyInitializer":{}
prettyvoid

¡Excelente! arreglado aquí también, esta es la respuesta
Hinotori

@prettyvoid ¿Por qué existen esas propiedades adicionales?
Robert Moon

12
@RobertMoon Si desea deshacerse de ellos, puede anotar su entidad con@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
prettyvoid

@prettyvoid Gracias. Encontré otra solución es cambiar LAZY a EAGER. ¿Afecta una actuación?
Robert Moon

69

Como se sugirió correctamente en respuestas anteriores, la carga diferida significa que cuando recupera su objeto de la base de datos, los objetos anidados no se recuperan (y pueden recuperarse más tarde cuando sea necesario).

Ahora Jackson intenta serializar el objeto anidado (== hacer JSON fuera de él), pero falla cuando encuentra JavassistLazyInitializer en lugar del objeto normal. Este es el error que ves. Ahora bien, ¿cómo solucionarlo?

Como sugirió anteriormente CP510, una opción es suprimir el error mediante esta línea de configuración:

spring.jackson.serialization.fail-on-empty-beans=false

Pero se trata de tratar los síntomas, no la causa . Para resolverlo con elegancia, debe decidir si necesita este objeto en JSON o no.

  1. Si necesita el objeto en JSON, elimine la FetchType.LAZYopción del campo que lo causa (también podría ser un campo en algún objeto anidado, no solo en la entidad raíz que está obteniendo).

  2. Si no necesita el objeto en JSON, anote el captador de este campo (o el campo en sí, si tampoco necesita aceptar valores entrantes) con @JsonIgnore, por ejemplo:

    // this field will not be serialized to/from JSON @JsonIgnore private NestedType secret;

Si tiene necesidades más complejas (por ejemplo, diferentes reglas para diferentes controladores REST que usan la misma entidad), puede usar vistas o filtrado de jackson o, para un caso de uso muy simple, buscar objetos anidados por separado.


16
Esta es la mejor respuesta. Aborde la raíz del problema, no se limite a enmascarar los síntomas para que el próximo programador tenga que ocuparse.
Andrew

2
Eres mi héroe.
Lay Leangsros

muchas gracias por explicar el motivo del error y brindar dos soluciones que tienen sentido
Mauricio Poppe

Gracias por esto, sin embargo, veo que springfox-swagger todavía me da un errorRangeError: Maximum call stack size exceeded
Pra_A

18

Puede usar el módulo adicional para Jackson que maneja la carga diferida de Hibernate.

Más información en https://github.com/FasterXML/jackson-datatype-hibernate que admite hibernación 3 y 4 por separado.


7
Esta debería ser realmente la respuesta correcta. Todas las otras respuestas ocultan el problema en lugar de resolverlo.
Ahmed Hassanien

¿Pero cómo utilizar este módulo? ¿Hay alguna guía para ello?
Anand Vaidya

Formas de configurar jackson-datatype-hibernate: stackoverflow.com/q/33727017
Chase

13

Creo que el problema es la forma en que recuperas la entidad.

Tal vez estés haciendo algo como esto:

Person p = (Person) session.load(Person.class, new Integer(id));

Intente usar el método en getlugar deload

Person p = (Person) session.get(Person.class, new Integer(id));

El problema es que con el método de carga solo se obtiene un proxy pero no el objeto real. El objeto proxy no tiene las propiedades ya cargadas, por lo que cuando ocurre la serialización, no hay propiedades para serializar. Con el método get, realmente obtiene el objeto real, este objeto podría de hecho ser serializado.


2
Me sucedió un error muy similar cuando usé getOne en lugar de findOne. En mi caso, mi repositorio de interfaz estaba ampliando JpaRepository. findOne funcionó bien sin anotaciones Json o cambios en application.properties
James Freitas

Al igual que James Freitas, también descubrí que getOne conducía al problema y, al usar en su lugar, por ejemplo, findById, se resolvió el problema sin necesidad de usar la anotación o la línea dada arriba en las propiedades de la aplicación
MI

1
Mi compañero de trabajo me aclaró y me dijo que: "La diferencia básica es que getOne se carga de forma diferida y findOne no", lo que significa, obviamente, que el primero no obtiene filas de datos de la base de datos, sino que solo crea referencias. Este último, en cambio, obtiene filas en realidad, que es el caso que a menudo se desea
MI

@JamesFreitas ¡SÍ! ¡Gracias! ¡Y gracias Carlos por llevar a James a esta linda conclusión!
Filip Savic

9

esto funciona para mi

@JsonIgnoreProperties({"hibernateLazyInitializer","handler"})

p.ej

@Entity
@Table(name = "user")
@Data
@NoArgsConstructor
@JsonIgnoreProperties({"hibernateLazyInitializer","handler"})
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private Date created;

}

5

Hay dos formas de solucionar el problema.

Camino 1 :

Agregar spring.jackson.serialization.fail-on-empty-beans=falsea application.properties

Camino 2 :

Úselo join fetchen la consulta JPQL para recuperar datos del objeto principal, consulte a continuación:

@Query(value = "select child from Child child join fetch child.parent Parent ",
           countQuery = "select count(*) from Child child join child.parent parent ")
public Page<Parent> findAll(Pageable pageable); 

¡Gracias! ¡La primera solución es genial!
Lucas Moyano Angelini

Camino 1. Genial. Gracias.
Lay Leangsros

3

Agregue esta anotación a la clase de entidad (modelo) que funciona para mí, esto causa una carga diferida a través del objeto proxy de hibernación.

@JsonIgnoreProperties ({"hibernateLazyInitializer", "controlador"})


2

Esta excepción

org.springframework.http.converter.HttpMessageNotWritableException

obteniendo porque, espero que sí, estás enviando una salida de respuesta como un objeto serializable.
Este es un problema que ocurre en primavera. Para superar este problema, envíe el objeto POJO como salida de respuesta.

Ejemplo:

    @Entity
    @Table(name="user_details")
    public class User implements Serializable{

        @Id
        @GeneratedValue(strategy= GenerationType.IDENTITY)
        @Column(name="id")
        private Integer id;

        @Column(name="user_name")
        private String userName;

        @Column(name="email_id")
        private String emailId;

        @Column(name="phone_no")
        private String phone;

//setter and getters

Clase POJO:

public class UserVO {

    private int Id;
    private String userName;
    private String emailId;
    private String phone;
    private Integer active;

//setter and getters

En el controlador, convierta los campos del objeto serilizable en campos de clase POJO y devuelva la clase pojo como salida.

         User u= userService.getdetials(); // get data from database

        UserVO userVo= new UserVO();  // created pojo class object

        userVo.setId(u.getId());
        userVo.setEmailId(u.getEmailId());
        userVo.setActive(u.getActive());
        userVo.setPhone(u.getPhone());
        userVo.setUserName(u.getUserName());
       retunr userVo;  //finally send pojo object as output.

Sí, estoy haciendo lo mismo, pero incluso haciendo lo mismo, obtengo el mismo error, porque estoy enviando datos asociados a objetos que no son más que configurar clases de entidad
Pra_A

2

En Hibernate 5.2 y superior, puede eliminar el proxy de hibernación como se muestra a continuación, le dará el objeto real para que pueda serializarlo correctamente:

Object unproxiedEntity = Hibernate.unproxy( proxy );

también llamará automáticamente de Hibernate.initializeantemano.
GMsoF

@Tim, debe agregarse antes de la serialización json. El problema ocurre porque se encuentra un objeto proxy de hibernación durante la serialización, por lo que una vez eliminado, la serialización estará bien. Si está utilizando un controlador de resorte, debe hacerlo antes de que finalice el controlador.
GMsoF

1

Para Hibernate, puede usar jackson-datatype-hibernate para acomodar la serialización / deserialización JSON con objetos de carga diferida.

Por ejemplo,

import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JacksonDatatypeHibernate5Configuration {

    // Register Jackson Hibernate5 Module to handle JSON serialization of lazy-loaded entities
    // Any beans of type com.fasterxml.jackson.databind.Module are automatically
    // registered with the auto-configured Jackson2ObjectMapperBuilder
    // https://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-mvc.html#howto-customize-the-jackson-objectmapper
    @Bean
    public Module hibernate5Module() {
        Hibernate5Module hibernate5Module = new Hibernate5Module();
        hibernate5Module.enable( Hibernate5Module.Feature.FORCE_LAZY_LOADING );
        hibernate5Module.disable( Hibernate5Module.Feature.USE_TRANSIENT_ANNOTATION );
        return hibernate5Module;
    }
}

Si usé esto, entonces obtengo can't parse JSON. Raw result:. ¿Alguna ayuda?
Pra_A

1

La solución está inspirada en la siguiente solución de @marco. También he actualizado su respuesta con estos datos.

El problema aquí es sobre la carga diferida de los subobjetos, donde Jackson solo encuentra proxies de hibernación, en lugar de objetos completos.

Así que nos quedan dos opciones: suprimir la excepción, como se hizo anteriormente en la respuesta más votada aquí, o asegurarse de que los objetos LazyLoad estén cargados.

Si elige optar por la última opción, la solución sería usar la biblioteca jackson-datatype y configurar la biblioteca para inicializar las dependencias de carga diferida antes de la serialización.

Agregué una nueva clase de configuración para hacer eso.

@Configuration
public class JacksonConfig extends WebMvcConfigurerAdapter {

@Bean
@Primary
public MappingJackson2HttpMessageConverter jacksonMessageConverter(){
    MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
    ObjectMapper mapper = new ObjectMapper();
    Hibernate5Module module = new Hibernate5Module();
    module.enable(Hibernate5Module.Feature.FORCE_LAZY_LOADING);
    mapper.registerModule(module);
    messageConverter.setObjectMapper(mapper);
    return messageConverter;
}

}

@Primaryse asegura de que no se utilice ninguna otra configuración de Jackson para inicializar otros beans. @Beanes como de costumbre. module.enable(Hibernate5Module.Feature.FORCE_LAZY_LOADING);es habilitar la carga diferida de las dependencias.

Precaución: esté atento a su impacto en el rendimiento. a veces, EAGER fetch ayuda, pero incluso si lo hace ansioso, todavía necesitará este código, porque los objetos proxy todavía existen para todas las demás asignaciones excepto@OneToOne

PD: Como comentario general, desalentaría la práctica de enviar el objeto de datos completo de vuelta en la respuesta de Json. Uno debería usar Dto's para esta comunicación y usar algún mapeador como maptruct para mapearlos. Esto le evita lagunas de seguridad accidentales, así como la excepción anterior.


1

Tengo el mismo problema ahora mismo. compruebe si corrige la recuperación en lazy con un @jsonIQgnore

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="teachers")
@JsonIgnoreProperties("course")
Teacher teach;

Simplemente elimine "(fetch = ...)" o la anotación "@jsonIgnore" y funcionará

@ManyToOne
@JoinColumn(name="teachers")
@JsonIgnoreProperties("course")
Teacher teach;

0

O puede configurar el asignador como:

// configuración personalizada para carga diferida

public static class HibernateLazyInitializerSerializer extends JsonSerializer<JavassistLazyInitializer> {

    @Override
    public void serialize(JavassistLazyInitializer initializer, JsonGenerator jsonGenerator,
            SerializerProvider serializerProvider)
            throws IOException, JsonProcessingException {
        jsonGenerator.writeNull();
    }
}

y configurar mapeador:

    mapper = new JacksonMapper();
    SimpleModule simpleModule = new SimpleModule(
            "SimpleModule", new Version(1,0,0,null)
    );
    simpleModule.addSerializer(
            JavassistLazyInitializer.class,
            new HibernateLazyInitializerSerializer()
    );
    mapper.registerModule(simpleModule);

0

Podría ser su relación de entidad de Hibernate la que causa el problema ... simplemente detenga la carga diferida de esa entidad relacionada ... por ejemplo ... Lo resolví a continuación estableciendo lazy = "false" para customerType.

<class name="Customer" table="CUSTOMER">
        <id name="custId" type="long">
            <column name="CUSTID" />
            <generator class="assigned" />
        </id>
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        <property name="phone" type="java.lang.String">
            <column name="PHONE" />
        </property>
        <property name="pan" type="java.lang.String">
            <column name="PAN" />
        </property>

        <many-to-one name="customerType" not-null="true" lazy="false"></many-to-one>
    </class>
</hibernate-mapping>

1
Informe en su publicación que puede ser una solución muy peligrosa en la aplicación de la vida real.
Panurg

0

Cambié (en la clase de modelo de anotación)

fetch = FetchType.LAZY

a

fetch = FetchType.EAGER

y funcionó de manera bonita ...

Quiéralo.


5
Si bien esto resuelve el problema, es muy peligroso. Cambiar la fetchestrategia de un modelo tiene varias consecuencias en el rendimiento. Con este cambio, traerá muchos más datos de la base de datos. Puede ser una solución válida, pero primero se necesita un estudio de desempeño.
João Menighin

1
Si sabe que es modelo y no tendrá muchas entidades relacionadas, puede gastar un poco de memoria y rendimiento para obtener una entidad completa. pero es bueno saber que siempre es mejor usar LAZY fetch
Sham Fiorin

0

Este es un problema con Jackson. Para evitar esto, indique a Jackson que no serialice la relación anidada o la clase anidada.

mira el siguiente ejemplo. La clase de dirección se asigna a las clases Ciudad , Estado y País , y el propio Estado apunta al País y al País apunta a la Región. Cuando obtenga los valores de dirección a través de la API REST de Spring boot, obtendrá el error anterior. Para evitarlo, simplemente serialice la clase asignada (que refleja JSON de nivel uno) e ignore las relaciones anidadas con @JsonIgnoreProperties(value = {"state"}), @JsonIgnoreProperties(value = {"country"})y @JsonIgnoreProperties(value = {"region"})

Esto evitará la excepción Lazyload junto con el error anterior. Utilice el siguiente código como ejemplo y cambie las clases de su modelo.

Dirección.java

@Entity
public class Address extends AbstractAuditingEntity
{
    private static final long serialVersionUID = 4203344613880544060L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @Column(name = "street_name")
    private String streetName;

    @Column(name = "apartment")
    private String apartment;

    @ManyToOne
    @JoinColumn(name = "city_id")
    @JsonIgnoreProperties(value = {"state"})
    private City city;

    @ManyToOne
    @JoinColumn(name = "state_id")
    @JsonIgnoreProperties(value = {"country"})
    private State state;

    @ManyToOne
    @JoinColumn(name = "country_id")
    @JsonIgnoreProperties(value = {"region"})
    private Country country;

    @ManyToOne
    @JoinColumn(name = "region_id")
    private Region region;

    @Column(name = "zip_code")
    private String zipCode;

    @ManyToOne
    @JoinColumn(name = "address_type_id", referencedColumnName = "id")
    private AddressType addressType;

}

City.java

@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = "city")
@Cache(region = "cityCache",usage = CacheConcurrencyStrategy.READ_WRITE)
@Data
public class City extends AbstractAuditingEntity
{
    private static final long serialVersionUID = -8825045541258851493L;

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name")
    //@Length(max = 100,min = 2)
    private String name;


    @ManyToOne
    @JoinColumn(name = "state_id")
    private State state;
}

State.java

@Entity
@Table(name = "state")
@Data
@EqualsAndHashCode(callSuper = true)
public class State extends AbstractAuditingEntity
{
    private static final long serialVersionUID = 5553856435782266275L;

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "code")
    private String code;

    @Column(name = "name")
    @Length(max = 200, min = 2)
    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "country_id")
    private Country country;

}

Country.java

@Entity
@Table(name = "country")
@Data
@EqualsAndHashCode(callSuper = true)
public class Country extends AbstractAuditingEntity
{
    private static final long serialVersionUID = 6396100319470393108L;

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name")
    @Length(max = 200, min = 2)
    private String name;

    @Column(name = "code")
    @Length(max = 3, min = 2)
    private String code;

    @Column(name = "iso_code")
    @Length(max = 3, min = 2)
    private String isoCode;

    @ManyToOne
    @JoinColumn(name = "region_id")
    private Region region;
}

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.