没有找到用于 org.hibernate.proxy.pojo.javass.Javassociation 类的序列化程序?

我工作的 SpringMVCHibernateJSON,但我得到这个错误。

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


}

我该怎么解决这个问题?

148091 次浏览

I had a similar problem with lazy loading via the hibernate proxy object. Got around it by annotating the class having lazy loaded private properties with:

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

I assume you can add the properties on your proxy object that breaks the JSON serialization to that annotation.

The problem is that entities are loaded lazily and serialization happens before they get loaded fully.

Hibernate.initialize(<your getter method>);

Try

implements interface Serializable

You could use the add-on module for Jackson which handles Hibernate lazy-loading.

More info on https://github.com/FasterXML/jackson-datatype-hibernate wich support hibernate 3 and 4 separately.

Just to add this in, I ran into this same issue, but the supplied answers did not work. I fixed it by taking the exception's suggestion and adding to the application.properties file...

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

I'm using Spring Boot v1.3 with Hibernate 4.3

It now serializes the entire object and nested objects.

EDIT: 2018

Since this still gets comments I'll clarify here. This absolutely only hides the error. The performance implications are there. At the time, I needed something to deliver and work on it later (which I did via not using spring anymore). So yes, listen to someone else if you really want to solve the issue. If you just want it gone for now go ahead and use this answer. It's a terrible idea, but heck, might work for you. For the record, never had a crash or issue again after this. But it is probably the source of what ended up being a SQL performance nightmare.

I think that the problem is the way that you retrieve the entity.

Maybe you are doing something like this:

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

Try using the method get instead of load

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

The problem is that with load method you get just a proxy but not the real object. The proxy object doesn't have the properties already loaded so when the serialization happens there are no properties to be serialized. With the get method you actually get the real object, this object could in fact be serialized.

As it is correctly suggested in previous answers, lazy loading means that when you fetch your object from the database, the nested objects are not fetched (and may be fetched later when required).

Now Jackson tries to serialize the nested object (== make JSON out of it), but fails as it finds JavassistLazyInitializer instead of normal object. This is the error you see. Now, how to solve it?

As suggested by CP510 previously, one option is to suppress the error by this line of configuration:

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

But this is dealing with the symptoms, not the cause. To solve it elegantly, you need to decide whether you need this object in JSON or not?

  1. Should you need the object in JSON, remove the FetchType.LAZY option from the field that causes it (it might also be a field in some nested object, not only in the root entity you are fetching).

  2. If do not need the object in JSON, annotate the getter of this field (or the field itself, if you do not need to accept incoming values either) with @JsonIgnore, for example:

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

Should you have more complex needs (e.g. different rules for different REST controllers using the same entity), you can use jackson views or filtering or for very simple use case, fetch nested objects separately.

Or you could configure mapper as :

// custom configuration for lazy loading

public static class HibernateLazyInitializerSerializer extends JsonSerializer<JavassistLazyInitializer> {


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

and configure mapper:

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

It could be your Hibernate entity relationship causing the issue...simply stop lazy loading of that related entity...for example...I resolved below by setting lazy="false" for 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>

This Exception

org.springframework.http.converter.HttpMessageNotWritableException

getting because, I hope so, your are sending response output as Serializable object.
This is problem occurring in spring. To over come this issue, send POJO object as response output.

Example :

    @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 class:

public class UserVO {


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


//setter and getters

In controller convert the serilizable object fields to POJO class fields and return pojo class as output.

         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.

In the Hibernate 5.2 and above, you able to remove the hibernate proxy as below, it will give you the actual object so you can serialize it properly:

Object unproxiedEntity = Hibernate.unproxy( proxy );

Add this Annotation to Entity Class (Model) that works for me this cause lazy loading via the hibernate proxy object.

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

I changed (in Annotation model class)

fetch = FetchType.LAZY

to

fetch = FetchType.EAGER

and worked in pretty way...

Love it.

For Hibernate you can use the jackson-datatype-hibernate project to accommodate JSON serialization/deserialization with lazy-loaded objects.

For example,

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

it works for me

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

e.g.

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


}

The solution is inspired from the below solution by @marco. I have also updated his answer with these datails.

The problem here is about lazy loading of the sub-objects, where Jackson only finds hibernate proxies, instead of full blown objects.

So we are left with two options - Suppress the exception, like done above in most voted answer here, or make sure that the LazyLoad objects are loaded.

If you choose to go with latter option, solution would be to use jackson-datatype library, and configure the library to initialize lazy-load dependencies prior to the serialization.

I added a new config class to do that.

@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 makes sure that no other Jackson config is used for initializing any other beans. @Bean is as usual. module.enable(Hibernate5Module.Feature.FORCE_LAZY_LOADING); is to enable Lazy loading of the dependencies.

Caution - Please watch for its performance impact. sometimes EAGER fetch helps, but even if you make it eager, you are still going to need this code, because proxy objects still exist for all other Mappings except @OneToOne

PS : As a general comment, I would discourage the practice of sending whole data object back in the Json response, One should be using Dto's for this communication, and use some mapper like mapstruct to map them. This saves you from accidental security loopholes, as well as above exception.

This is an issue with Jackson. To prevent this, instruct Jackson not to serialize nested relationship or nested class.

Look at the following example. Address class mapped to City, State, and Country classes and the State itself is pointing to Country and Country pointing to Region. When your get address values through Spring boot REST API you will get the above error. To prevent it, just serialize mapped class ( which reflects level one JSON) and ignore nested relationships with @JsonIgnoreProperties(value = {"state"}),@JsonIgnoreProperties(value = {"country"}) and @JsonIgnoreProperties(value = {"region"})

This will prevent Lazyload exception along with the above error. Use the below code as an example and change your model classes.

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

I've the same problem right now. check if you fix fetch in lazy with a @jsonIQgnore

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

Just delete the "(fetch=...)" or the annotation "@jsonIgnore" and it will work

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

There are two ways to fix the problem.

Way 1:

spring.jackson.serialization.fail-on-empty-beans=false into application.properties

Way 2:

Use join fetch in JPQL query to retrieve parent object data, see below:

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

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

it works for me