Je travaille sur SpringMVC
, Hibernate
& JSON
mais cette erreur me concerne.
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) )
S'il vous plaît vérifier mon entité ci-dessous
@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;
}
}
Comment puis-je résoudre ça?
J'ai eu un problème similaire avec le chargement différé via l'objet proxy hibernate. Pour contourner le problème, annotez à la classe les propriétés privées chargées paresseuses avec:
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
Je suppose que vous pouvez ajouter les propriétés de votre objet proxy qui cassent la sérialisation JSON à cette annotation.
Le problème est que les entités sont chargées paresseusement et que la sérialisation a lieu avant leur chargement complet.
Hibernate.initialize(<your getter method>);
Juste pour ajouter ceci, j'ai rencontré le même problème, mais les réponses fournies ne fonctionnaient pas. Je l'ai corrigé en prenant la suggestion de l'exception et en ajoutant au fichier application.properties ...
spring.jackson.serialization.fail-on-empty-beans=false
J'utilise Spring Boot v1.3 avec Hibernate 4.3
Il sérialise maintenant l'objet entier et les objets imbriqués.
EDIT: 2018
Comme cela fait toujours l'objet de commentaires, je vais clarifier ici. Ce absolument ne cache que l'erreur. Les conséquences sur les performances sont là. À l'époque, j'avais besoin de quelque chose à livrer et à y travailler plus tard (ce que j'ai fait en n'utilisant plus le printemps). Alors oui, écoutez quelqu'un d'autre si vous voulez vraiment résoudre le problème. Si vous voulez seulement que ce soit parti pour l'instant, allez-y et utilisez cette réponse. C'est une idée terrible, mais bon, ça pourrait marcher pour vous. Pour mémoire, plus jamais, aucun incident ne s'est produit. Mais c'est probablement la source de ce qui a fini par être un cauchemar de performance SQL.
Comme il est correctement suggéré dans les réponses précédentes, le chargement différé signifie que lorsque vous extrayez votre objet de la base de données, les objets imbriqués ne sont pas récupérés (et peuvent être récupérés ultérieurement si nécessaire).
Maintenant, Jackson essaie de sérialiser l'objet imbriqué (== en fait un JSON), mais échoue car il trouve JavassistLazyInitializer au lieu d'un objet normal. C'est l'erreur que vous voyez. Maintenant, comment le résoudre?
Comme suggéré par le CP510 précédemment, une option consiste à supprimer l’erreur par cette ligne de configuration:
spring.jackson.serialization.fail-on-empty-beans=false
Mais c’est traitant des symptômes, pas de la causePour le résoudre avec élégance, vous devez décider si vous avez besoin de cet objet en JSON ou non.
Si vous avez besoin de l'objet en JSON, supprimez l'option FetchType.LAZY
du champ qui le provoque (il peut également s'agir d'un champ dans un objet imbriqué, pas seulement dans l'entité racine que vous extrayez).
Si vous n'avez pas besoin de l'objet en JSON, annotez le getter de ce champ (ou le champ lui-même, si vous n'avez pas non plus besoin d'accepter les valeurs entrantes) avec @JsonIgnore
, par exemple:
// this field will not be serialized to/from JSON
@JsonIgnore
private NestedType secret;
Si vous avez des besoins plus complexes (par exemple, des règles différentes pour différents contrôleurs REST utilisant la même entité), vous pouvez utiliser jackson vues ou filtrage ou pour un cas d'utilisation très simple, récupérez les objets imbriqués séparément .
Je pense que le problème est la façon dont vous récupérez l'entité.
Peut-être que vous faites quelque chose comme ça:
Person p = (Person) session.load(Person.class, new Integer(id));
Essayez d'utiliser la méthode get
au lieu de load
Person p = (Person) session.get(Person.class, new Integer(id));
Le problème est qu'avec la méthode de chargement, vous obtenez uniquement un proxy mais pas l'objet réel. Les propriétés de l'objet proxy ne sont pas déjà chargées. Par conséquent, lorsque la sérialisation est effectuée, aucune propriété ne doit être sérialisée. Avec la méthode get, vous obtenez réellement l'objet réel, cet objet peut en fait être sérialisé.
Vous pouvez utiliser le module complémentaire de Jackson, qui gère le chargement paresseux d’Hibernate.
Plus d'informations sur https://github.com/FasterXML/jackson-datatype-hibernate qui prennent en charge l'hibernation 3 et 4 séparément.
ça marche pour moi
@JsonIgnoreProperties({"hibernateLazyInitializer","handler"})
par exemple.
@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;
}
Cette exception
org.springframework.http.converter.HttpMessageNotWritableException
get parce que, je l’espère, vous envoyez la réponse en tant qu’objet Serializable.
Ce problème se produit au printemps. Pour résoudre ce problème, envoyez un objet POJO en tant que résultat de la réponse.
Exemple :
@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
Classe POJO:
public class UserVO {
private int Id;
private String userName;
private String emailId;
private String phone;
private Integer active;
//setter and getters
Dans l’automate, convertissez les champs d’objet sérialisables en champs de classe POJO et renvoyez-les en tant que sortie.
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.
Dans Hibernate 5.2 et versions ultérieures, vous pouvez supprimer le proxy hibernate comme indiqué ci-dessous. Il vous donnera l'objet réel afin que vous puissiez le sérialiser correctement:
Object unproxiedEntity = Hibernate.unproxy( proxy );
Ajoutez cette annotation à la classe d'entité (modèle) qui fonctionne pour moi. Cela cause un chargement paresseux via l'objet proxy hibernate.
@JsonIgnoreProperties ({"hibernateLazyInitializer", "gestionnaire"})
La solution est inspirée de la solution ci-dessous de @marco. J'ai également mis à jour sa réponse avec ces datails.
Le problème ici concerne le chargement paresseux des sous-objets, où Jackson ne trouve que des mandataires en veille prolongée, à la place des objets complets.
Il nous reste donc deux options: supprimer l'exception, comme c'est le cas dans la réponse la plus votée ci-dessus, ou vous assurer que les objets LazyLoad sont chargés.
Si vous choisissez d'utiliser cette dernière option, la solution consisterait à utiliser la bibliothèque jackson-datatype et à la configurer pour initialiser les dépendances de chargement différé avant la sérialisation.
J'ai ajouté une nouvelle classe de configuration pour le faire.
@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
s'assure qu'aucune autre configuration de Jackson n'est utilisée pour initialiser d'autres beans. @Bean
est comme d'habitude. module.enable(Hibernate5Module.Feature.FORCE_LAZY_LOADING);
consiste à activer le chargement différé des dépendances.
Attention - Surveillez son impact sur les performances. EAGER fetch aide parfois, mais même si vous le rendez désireux, vous aurez toujours besoin de ce code, car des objets proxy existent toujours pour tous les autres mappages, à l'exception de @OneToOne
.
PS: En règle générale, je déconseillerais de renvoyer un objet de données complet dans la réponse Json. On devrait utiliser Dto pour cette communication et utiliser un mappeur tel que mapstruct pour les mapper. Cela vous évite des failles de sécurité accidentelles, ainsi que l'exception ci-dessus.
J'ai changé (dans la classe de modèle d'annotation)
fetch = FetchType.LAZY
à
fetch = FetchType.EAGER
et a travaillé de belle façon ...
Aimer.
Ou vous pouvez configurer le mappeur comme:
// configuration personnalisée pour chargement paresseux
public static class HibernateLazyInitializerSerializer extends JsonSerializer<JavassistLazyInitializer> {
@Override
public void serialize(JavassistLazyInitializer initializer, JsonGenerator jsonGenerator,
SerializerProvider serializerProvider)
throws IOException, JsonProcessingException {
jsonGenerator.writeNull();
}
}
et configurez le mappeur:
mapper = new JacksonMapper();
SimpleModule simpleModule = new SimpleModule(
"SimpleModule", new Version(1,0,0,null)
);
simpleModule.addSerializer(
JavassistLazyInitializer.class,
new HibernateLazyInitializerSerializer()
);
mapper.registerModule(simpleModule);
Cela pourrait être votre relation d'entité Hibernate à l'origine du problème ... arrêtez simplement le chargement paresseux de cette entité associée ... par exemple ... J'ai résolu ci-dessous en définissant lazy = "false" pour 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>
C'est un problème avec Jackson. Pour éviter cela, demandez à Jackson de ne pas sérialiser la relation imbriquée ou la classe imbriquée.
Regardez l'exemple suivant. Adresse classe mappée sur Ville, État, et Pays classes et l'État lui-même désigne le pays et le pays pointant. à la région. Lorsque vous obtenez les valeurs d'adresse via l'API Spring Boot REST, vous obtenez l'erreur ci-dessus. Pour l'éviter, il suffit de sérialiser la classe mappée (qui reflète le JSON de niveau 1) et d'ignorer les relations imbriquées avec @JsonIgnoreProperties(value = {"state"})
, @JsonIgnoreProperties(value = {"country"})
et @JsonIgnoreProperties(value = {"region"})
.
Cela empêchera l'exception Lazyload avec l'erreur ci-dessus. Utilisez le code ci-dessous comme exemple et modifiez vos classes de modèle.
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;
}
Pays.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;
}
Pour Hibernate, vous pouvez utiliser le projet jackson-datatype-hibernate pour prendre en charge la sérialisation/désérialisation JSON avec des objets chargés paresseux.
Par exemple,
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;
}
}