Pracuję nad SpringMVC
, Hibernate
i JSON
ale ja dostaję ten błąd.
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) )
Sprawdź poniżej mój podmiot
@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;
}
}
Jak mogę to rozwiązać?
Odpowiedzi:
Miałem podobny problem z leniwym ładowaniem przez obiekt hibernacji proxy. Obejdź ten problem, dodając adnotacje do klasy, która ma leniwie ładowane właściwości prywatne:
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
Zakładam, że możesz dodać właściwości do obiektu proxy, które przerywają serializację JSON do tej adnotacji.
Problem polega na tym, że jednostki są ładowane leniwie, a serializacja ma miejsce, zanim zostaną w pełni załadowane.
Hibernate.initialize(<your getter method>);
źródło
Aby to dodać, napotkałem ten sam problem, ale podane odpowiedzi nie zadziałały. Naprawiłem to, biorąc sugestię wyjątku i dodając do pliku application.properties ...
spring.jackson.serialization.fail-on-empty-beans=false
Używam Spring Boot v1.3 z Hibernate 4.3
Teraz serializuje cały obiekt i obiekty zagnieżdżone.
EDYCJA: 2018
Ponieważ nadal pojawiają się komentarze, wyjaśnię tutaj. To absolutnie tylko ukrywa błąd. Istnieją implikacje dotyczące wydajności. W tamtym czasie potrzebowałem czegoś, co mógłbym dostarczyć i popracować nad tym później (co zrobiłem, nie używając już wiosny). Więc tak, posłuchaj kogoś innego, jeśli naprawdę chcesz rozwiązać problem. Jeśli na razie chcesz, żeby to zniknęło, użyj tej odpowiedzi. To okropny pomysł, ale do cholery, może ci się udać. Dla przypomnienia, po tym nigdy nie wystąpiła awaria ani problem. Ale prawdopodobnie jest to źródło tego, co skończyło się koszmarem wydajności SQL.
źródło
"handler":{},"hibernateLazyInitializer":{}
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
Jak słusznie sugerowano w poprzednich odpowiedziach, leniwe ładowanie oznacza, że podczas pobierania obiektu z bazy danych zagnieżdżone obiekty nie są pobierane (i mogą zostać pobrane później, gdy będzie to wymagane).
Teraz Jackson próbuje serializować zagnieżdżony obiekt (== zrobić z niego JSON), ale kończy się niepowodzeniem, ponieważ znajduje JavassistLazyInitializer zamiast normalnego obiektu. To jest błąd, który widzisz. Jak to rozwiązać?
Jak sugerował wcześniej CP510, jedną z opcji jest wyeliminowanie błędu w tej linii konfiguracji:
spring.jackson.serialization.fail-on-empty-beans=false
Ale to dotyczy objawów, a nie przyczyny . Aby elegancko to rozwiązać, musisz zdecydować, czy potrzebujesz tego obiektu w JSON, czy nie?
Jeśli potrzebujesz obiektu w formacie JSON, usuń
FetchType.LAZY
opcję z pola, które go powoduje (może to być również pole w jakimś zagnieżdżonym obiekcie, a nie tylko w pobieranej encji głównej).Jeśli nie potrzebujesz obiektu w formacie JSON, dodaj adnotację do gettera tego pola (lub samego pola, jeśli nie musisz również akceptować przychodzących wartości)
@JsonIgnore
, na przykład:// this field will not be serialized to/from JSON @JsonIgnore private NestedType secret;
Jeśli masz bardziej złożone potrzeby (np. Różne reguły dla różnych kontrolerów REST używających tej samej encji), możesz użyć widoków Jacksona lub filtrowania lub w bardzo prostym przypadku użycia, pobrać zagnieżdżone obiekty oddzielnie.
źródło
RangeError: Maximum call stack size exceeded
Możesz użyć modułu dodatkowego dla Jacksona, który obsługuje leniwe ładowanie Hibernate.
Więcej informacji na https://github.com/FasterXML/jackson-datatype-hibernate, które obsługują osobno hibernację 3 i 4.
źródło
Myślę, że problemem jest sposób, w jaki odzyskujesz jednostkę.
Może robisz coś takiego:
Person p = (Person) session.load(Person.class, new Integer(id));
Spróbuj użyć tej metody
get
zamiastload
Person p = (Person) session.get(Person.class, new Integer(id));
Problem polega na tym, że przy metodzie ładowania otrzymujesz tylko proxy, ale nie prawdziwy obiekt. Obiekt proxy nie ma już załadowanych właściwości, więc gdy nastąpi serializacja, nie ma właściwości do serializacji. Za pomocą metody get otrzymujesz rzeczywisty obiekt, który w rzeczywistości może być serializowany.
źródło
mi to pasuje
@JsonIgnoreProperties({"hibernateLazyInitializer","handler"})
na przykład
@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; }
źródło
Istnieją dwa sposoby rozwiązania problemu.
Sposób 1 :
Dodaj
spring.jackson.serialization.fail-on-empty-beans=false
do application.propertiesSposób 2 :
Użyj
join fetch
w zapytaniu JPQL, aby pobrać dane obiektu nadrzędnego, patrz poniżej:@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);
źródło
Dodaj tę adnotację do klasy jednostki (modelu), która działa dla mnie, powoduje to leniwe ładowanie przez obiekt proxy hibernacji.
źródło
Ten wyjątek
otrzymuję, ponieważ mam nadzieję, że wysyłasz odpowiedź jako obiekt serializowalny.
To problem występujący wiosną. Aby rozwiązać ten problem, wyślij obiekt POJO jako wynik odpowiedzi.
Przykład:
@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
Klasa POJO:
public class UserVO { private int Id; private String userName; private String emailId; private String phone; private Integer active; //setter and getters
W kontrolerze przekonwertuj pola obiektów z możliwością serilizacji na pola klasy POJO i jako dane wyjściowe zwróć klasę 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.
źródło
W wersji Hibernate 5.2 i nowszych można usunąć serwer proxy hibernacji, jak poniżej, poda on rzeczywisty obiekt, aby można było go poprawnie serializować:
Object unproxiedEntity = Hibernate.unproxy( proxy );
źródło
Hibernate.initialize
wcześniej.W przypadku Hibernate można użyć projektu jackson-datatype-hibernate , aby uwzględnić serializację / deserializację JSON z obiektami ładowanymi z opóźnieniem.
Na przykład,
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; } }
źródło
can't parse JSON. Raw result:
. Jakaś pomoc?Rozwiązanie jest inspirowane poniższym rozwiązaniem autorstwa @marco. Zaktualizowałem również jego odpowiedź tymi danymi.
Problem tutaj polega na leniwym ładowaniu podobiektów, gdzie Jackson znajduje tylko hibernowane proxy, zamiast pełnowymiarowych obiektów.
Zostały nam więc dwie opcje - Pomiń wyjątek, jak zrobiono powyżej w większości głosowanych odpowiedzi tutaj, lub upewnij się, że obiekty LazyLoad są załadowane.
Jeśli zdecydujesz się na drugą opcję, rozwiązaniem byłoby użycie biblioteki jackson-datatype i skonfigurowanie biblioteki tak, aby zainicjowała zależności lazy-load przed serializacją.
W tym celu dodałem nową klasę konfiguracyjną.
@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
upewnia się, że żadna inna konfiguracja Jacksona nie jest używana do inicjalizacji innych beanów.@Bean
jest jak zwykle.module.enable(Hibernate5Module.Feature.FORCE_LAZY_LOADING);
jest włączenie leniwego ładowania zależności.Uwaga - zwróć uwagę na wpływ na wydajność. czasami pomaga pobieranie EAGER, ale nawet jeśli sprawisz, że będzie chętny, nadal będziesz potrzebować tego kodu, ponieważ obiekty proxy nadal istnieją dla wszystkich innych mapowań z wyjątkiem
@OneToOne
PS: Jako ogólny komentarz, odradzałbym praktykę wysyłania całego obiektu danych z powrotem w odpowiedzi Json. Do tej komunikacji należy używać Dto i użyć jakiegoś mapera, takiego jak mapstruct do ich mapowania. Chroni to przed przypadkowymi lukami w zabezpieczeniach, a także powyższym wyjątkiem.
źródło
Mam teraz ten sam problem. sprawdź, czy naprawiasz leniwe pobieranie za pomocą @jsonIQgnore
@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name="teachers") @JsonIgnoreProperties("course") Teacher teach;
Po prostu usuń „(fetch = ...)” lub adnotację „@jsonIgnore” i zadziała
@ManyToOne @JoinColumn(name="teachers") @JsonIgnoreProperties("course") Teacher teach;
źródło
Lub możesz skonfigurować mappera jako:
// niestandardowa konfiguracja dla leniwego ładowania
public static class HibernateLazyInitializerSerializer extends JsonSerializer<JavassistLazyInitializer> { @Override public void serialize(JavassistLazyInitializer initializer, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException { jsonGenerator.writeNull(); } }
i skonfiguruj mappera:
mapper = new JacksonMapper(); SimpleModule simpleModule = new SimpleModule( "SimpleModule", new Version(1,0,0,null) ); simpleModule.addSerializer( JavassistLazyInitializer.class, new HibernateLazyInitializerSerializer() ); mapper.registerModule(simpleModule);
źródło
Problem może być spowodowany przez relację encji Hibernate ... po prostu zatrzymaj leniwe ładowanie tego powiązanego obiektu ... na przykład ... Rozwiązałem to poniżej, ustawiając lazy = "false" dla 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>
źródło
Zmieniłem (w klasie modelu adnotacji)
do
i działał w ładny sposób ...
Kocham to.
źródło
fetch
strategii w modelu ma kilka konsekwencji dla wydajności. Dzięki tej zmianie pobierasz znacznie więcej danych z bazy danych. Może to być prawidłowe rozwiązanie, ale najpierw potrzebne jest badanie wydajności.To jest problem z Jacksonem. Aby temu zapobiec, poinstruuj Jacksona, aby nie serializował relacji zagnieżdżonej ani klasy zagnieżdżonej.
Spójrz na poniższy przykład. Klasa adresu jest odwzorowana na klasy Miasto , Stan i Kraj , a sam stan wskazuje na Kraj i Kraj wskazujący na Region. Gdy otrzymujesz wartości adresu za pomocą Spring boot REST API, otrzymasz powyższy błąd. Aby temu zapobiec, po prostu serialize odwzorowane klasa (która odzwierciedla poziom jednego JSON) i zignorować zagnieżdżonych relacji z
@JsonIgnoreProperties(value = {"state"})
,@JsonIgnoreProperties(value = {"country"})
i@JsonIgnoreProperties(value = {"region"})
Zapobiegnie to wyjątkowi Lazyload wraz z powyższym błędem. Użyj poniższego kodu jako przykładu i zmień klasy modelu.
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; }
źródło
@JsonIgnoreProperties ({"hibernateLazyInitializer", "handler"})
mi to pasuje
źródło
Próbować
implements interface Serializable
źródło