Не найден сериализатор для класса org.hibernate.proxy.pojo.javassist.Javassist?


95

Я работаю над SpringMVC, Hibernateи , JSONно я получаю эту ошибку.

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) ) 

Пожалуйста, проверьте мою сущность ниже

    @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;
    }

    }

Как я могу это решить?


Пожалуйста, покажите stacktrace и код, в котором происходит исключение
geoand

Не зная, что пытается сделать ваш код, отладить его немного сложно, но вы, вероятно, захотите проверить github.com/FasterXML/jackson-datatype-hibernate, так как вы используете Jackson и Hibernate
geoand

Вы пытаетесь создать JSON из этого класса? В этом случае сериализатор JSON пытается записать все свойства, а также HashSet ваших отношений «многие ко многим»; это делает исключение ленивого инициализатора
Анджело

тот же вопрос можно найти на stackoverflow.com/questions/4362104/…
Matrix Buster

@ user2963481 ... Хороший и очень полезный вопрос, братан.
Brain

Ответы:


197

У меня была аналогичная проблема с ленивой загрузкой через прокси-объект гибернации. Обошли его, добавив аннотации к классу с лениво загруженными частными свойствами:

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

Я предполагаю, что вы можете добавить свойства своего прокси-объекта, которые нарушают сериализацию JSON, в эту аннотацию.

Проблема в том, что объекты загружаются лениво и сериализация происходит до того, как они загрузятся полностью.

Hibernate.initialize(<your getter method>);

3
У меня это тоже сработало ... Есть ли объяснение этому странному поведению?
Виктор

7
@ ankur-singhal вы должны подчеркнуть, что эта аннотация нужна для вложенного класса, а НЕ для вызывающего класса.
Darwayne

1
Yahooo ... это сработало. Я пробовал несколько раз и работал. Я пробовал несколько способов и BoooM. Работал.
Brain

2
Спасибо, у меня тоже сработало. Вам нужно добавить эту аннотацию к объектам, которые обращаются к другим объектам, имеющим ленивую загрузку beans.
Shafqat Shafi

3
Думаю, это неправильное решение вопроса. При сериализации мы ожидаем, что в результате будет возвращен либо полный подобъект, либо, по крайней мере, первичный ключ подобъекта. Добавление этих аннотаций просто подавит ошибку, но не даст желаемых результатов.
Ананд Вайдья

84

Чтобы добавить это, я столкнулся с той же проблемой, но предоставленные ответы не сработали. Я исправил это, взяв предложение об исключении и добавив в файл application.properties ...

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

Я использую Spring Boot v1.3 с Hibernate 4.3

Теперь он сериализует весь объект и вложенные объекты.

РЕДАКТИРОВАТЬ: 2018

Поскольку это все еще получает комментарии, я уточню здесь. Это абсолютно только скрывает ошибку. Последствия для производительности есть. В то время мне нужно было что-то доставить и поработать над этим позже (что я и сделал, перестав использовать Spring). Так что да, послушайте кого-нибудь еще, если вы действительно хотите решить проблему. Если вы просто хотите, чтобы он ушел, используйте этот ответ. Это ужасная идея, но, черт возьми, может сработать для вас. Для справки, после этого больше никогда не возникало сбоев или проблем. Но, вероятно, это источник того, что в конечном итоге стало кошмаром производительности SQL.


8
Решает проблему, но json будет содержать два дополнительных ненужных свойства"handler":{},"hibernateLazyInitializer":{}
prettyvoid

большой! исправлено и здесь, вот ответ
Хинотори

@prettyvoid Почему существуют эти дополнительные свойства?
Роберт Мун

12
@RobertMoon Если вы хотите избавиться от них, вы можете аннотировать свою сущность с помощью@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
prettyvoid

@prettyvoid Спасибо. Я нашел другое решение - сменить LAZY на EAGER. Это влияет на производительность?
Роберт Мун

69

Как правильно предложено в предыдущих ответах, ленивая загрузка означает, что при извлечении объекта из базы данных вложенные объекты не извлекаются (и могут быть извлечены позже, когда это необходимо).

Теперь Джексон пытается сериализовать вложенный объект (== сделать из него JSON), но терпит неудачу, поскольку находит JavassistLazyInitializer вместо обычного объекта. Это ошибка, которую вы видите. Теперь, как это решить?

Как было предложено ранее CP510, один из вариантов - подавить ошибку с помощью этой строки конфигурации:

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

Но это связано с симптомами, а не с причиной . Чтобы решить ее элегантно, вам нужно решить, нужен ли вам этот объект в JSON или нет?

  1. Если вам нужен объект в JSON, удалите FetchType.LAZYпараметр из поля, которое его вызывает (это также может быть поле в каком-то вложенном объекте, а не только в корневом объекте, который вы извлекаете).

  2. Если объект в JSON не нужен, аннотируйте получатель этого поля (или само поле, если вам тоже не нужно принимать входящие значения) @JsonIgnore, например:

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

Если у вас есть более сложные потребности (например , различные правила для различных контроллеров REST , используя один и тот же объект), вы можете использовать Джексон взгляды или фильтрацию или для очень простого случая использования, принесите вложенные объекты отдельно.


16
Это лучший ответ. Устраните корень проблемы, а не просто маскируйте симптомы для следующего программиста, с которым придется иметь дело.
Эндрю

2
Ты мой герой.
Lay Leangsros

Большое спасибо за объяснение причины ошибки и предоставление двух
Маурисио Поппе

Спасибо за это, однако я вижу, что springfox-swagger все еще выдает ошибкуRangeError: Maximum call stack size exceeded
Pra_A

18

Вы можете использовать дополнительный модуль для Jackson, который обрабатывает отложенную загрузку Hibernate.

Дополнительная информация на https://github.com/FasterXML/jackson-datatype-hibernate, который поддерживает спящий режим 3 и 4 отдельно.


7
Это должен быть действительно правильный ответ. Все остальные ответы скрывают проблему, а не решают ее.
Ахмед Хассаниен

Но как пользоваться этим модулем? есть какое-нибудь руководство для этого?
Ананд Вайдья

Способы настройки jackson-datatype-hibernate: stackoverflow.com/q/33727017
Чейз

13

Я думаю, что проблема в том, как вы извлекаете объект.

Может быть, вы делаете что-то вроде этого:

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

Попробуйте использовать метод getвместоload

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

Проблема в том, что с методом загрузки вы получаете просто прокси, а не реальный объект. Прокси-объект не имеет уже загруженных свойств, поэтому при сериализации нет свойств для сериализации. С помощью метода get вы фактически получаете реальный объект, этот объект фактически может быть сериализован.


2
Очень похожая ошибка случилась со мной, когда я использовал getOne вместо findOne. В моем случае репозиторий интерфейса расширял JpaRepository. findOne работал хорошо без каких-либо аннотаций Json или изменений в application.properties
Джеймс Фрейтас

Как и Джеймс Фрейтас, я также обнаружил, что getOne привел к проблеме, и, используя вместо этого, например, findById, проблема была решена без необходимости использовать аннотацию или указанную выше строку в свойствах приложения
MY

1
Мой товарищ по работе разъяснил мне, что: «Основное различие в том, что getOne загружается лениво, а findOne - нет» - это, очевидно, означает, что первый фактически не получает строки данных из базы данных, а просто создает ссылки. Последнее, а получает строки на самом деле, что это случай , который часто требуется
MY

@JamesFreitas ДА! Спасибо! И спасибо, Карлос, за то, что привел Джеймса к такому хорошему выводу!
Филип Савич,

9

меня устраивает

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

например

@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

Есть два способа решить проблему.

Способ 1 :

Добавить spring.jackson.serialization.fail-on-empty-beans=falseв application.properties

Способ 2 :

Используйте join fetchв запросе JPQL для получения данных родительского объекта, см. Ниже:

@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); 

Благодарность! Первое решение отличное!
Лукас Мояно Анджелини,

Способ 1. Отлично. Спасибо.
Lay Leangsros,

3

Добавьте эту аннотацию к классу сущности (модели), которая работает для меня, это вызывает ленивую загрузку через прокси-объект спящего режима.

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


2

Это исключение

org.springframework.http.converter.HttpMessageNotWritableException

получаю, потому что, я надеюсь, вы отправляете ответ как объект Serializable.
Это проблема, возникающая весной. Чтобы решить эту проблему, отправьте объект POJO в качестве результата ответа.

Пример :

    @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

POJO класс:

public class UserVO {

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

//setter and getters

В контроллере преобразуйте серилизуемые поля объекта в поля класса POJO и верните класс pojo в качестве вывода.

         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.

Да, я делаю то же самое, но даже делаю то же самое, я получаю ту же ошибку, потому что я отправляю данные, связанные с объектами, которые являются не чем иным, как установкой классов сущностей
Pra_A 06

2

В Hibernate 5.2 и выше вы можете удалить прокси-сервер гибернации, как показано ниже, он предоставит вам фактический объект, чтобы вы могли правильно его сериализовать:

Object unproxiedEntity = Hibernate.unproxy( proxy );

он также будет автоматически звонить Hibernate.initializeзаранее.
Sam YC

@Tim, его нужно добавить перед сериализацией json. Проблема возникает из-за того, что во время сериализации обнаружен прокси-объект гибернации, поэтому после его удаления сериализация будет в порядке. Если вы используете пружинный контроллер, вы должны сделать это до завершения контроллера.
Sam YC

1

Для Hibernate вы можете использовать проект jackson-datatype-hibernate, чтобы обеспечить сериализацию / десериализацию JSON с лениво загруженными объектами.

Например,

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;
    }
}

Если бы я использовал это, то получу can't parse JSON. Raw result:. Любая помощь?
Pra_A 06

1

Решение основано на приведенном ниже решении @marco. Я также обновил его ответ этими данными.

Проблема здесь в отложенной загрузке подобъектов, когда Джексон находит только прокси-серверы гибернации, а не полноценные объекты.

Таким образом, у нас остается два варианта: подавить исключение, как это сделано выше в ответе, за который проголосовали большинство здесь, или убедиться, что объекты LazyLoad загружены.

Если вы выберете последний вариант, решение будет заключаться в использовании библиотеки jackson-datatype и настройке библиотеки на инициализацию зависимостей отложенной загрузки до сериализации.

Для этого я добавил новый класс конфигурации.

@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;
}

}

@Primaryгарантирует, что никакая другая конфигурация Джексона не используется для инициализации других bean-компонентов. @Beanкак обычно. module.enable(Hibernate5Module.Feature.FORCE_LAZY_LOADING);- включить отложенную загрузку зависимостей.

Внимание - обратите внимание на его влияние на производительность. иногда EAGER fetch помогает, но даже если вы сделаете это с нетерпением, вам все равно понадобится этот код, потому что прокси-объекты все еще существуют для всех других сопоставлений, кроме@OneToOne

PS: В качестве общего комментария я бы не одобрил практику отправки всего объекта данных обратно в ответ Json. Для этого следует использовать Dto и использовать какой-нибудь картограф, например mapstruct, для их сопоставления. Это избавит вас от случайных лазеек в системе безопасности, а также от вышеуказанного исключения.


1

У меня сейчас такая же проблема. проверьте, исправляете ли вы выборку в ленивом режиме с помощью @jsonIQgnore

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

Просто удалите «(fetch = ...)» или аннотацию «@jsonIgnore», и все заработает.

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

0

Или вы можете настроить mapper как:

// настраиваемая конфигурация для отложенной загрузки

public static class HibernateLazyInitializerSerializer extends JsonSerializer<JavassistLazyInitializer> {

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

и настройте маппер:

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

0

Возможно, проблема связана с вашими отношениями сущностей Hibernate ... просто прекратите ленивую загрузку этой связанной сущности ... например ... Я решил ниже, установив lazy = "false" для 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
пожалуйста, сообщите в своем сообщении, что это может быть очень опасным решением в реальном приложении.
panurg 02

0

Я изменил (в классе модели аннотации)

fetch = FetchType.LAZY

к

fetch = FetchType.EAGER

и сработало красиво ...

Любить это.


5
Хотя это и решает проблему, это очень опасно. Изменение fetchстратегии модели имеет несколько последствий для производительности. С этим изменением вы получаете гораздо больше данных из базы данных. Это может быть правильное решение, но сначала необходимо исследование производительности.
Жоао Менигин

1
Если вы знаете, что вы модель и у вас не будет много связанных сущностей, вы можете потратить немного памяти и производительности, чтобы получить полную сущность. но полезно знать, что всегда лучше использовать LAZY fetch
Sham Fiorin

0

Это проблема с Джексоном. Чтобы предотвратить это, проинструктируйте Джексона не сериализовать вложенную связь или вложенный класс.

Взгляните на следующий пример. Класс адреса сопоставлен с классами City , State и Country, а сам State указывает на Country, а Country указывает на Region. Когда вы получите значения адреса через Spring boot REST API, вы получите указанную выше ошибку. Чтобы предотвратить это, просто сериализуйте сопоставленный класс (который отражает JSON первого уровня) и игнорируйте вложенные отношения с @JsonIgnoreProperties(value = {"state"}), @JsonIgnoreProperties(value = {"country"})и @JsonIgnoreProperties(value = {"region"})

Это предотвратит исключение Lazyload вместе с указанной выше ошибкой. Используйте приведенный ниже код в качестве примера и измените классы вашей модели.

Address.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;
}


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.