Celtik
Celtik

Reputation: 347

Hibernate lazy loading throws JsonMappingException

I know there are many questions about this same problem I will tell you, but none of them has been achieved to solve my problem yet.

I've implemented a service which has to access to an object property with LAZY loading. This is a simplification of the class:

@Entity
public class Treatment {

private Long treatmentId;
private UserAccount patient;
private Regimen regimen;
private Medicine medicine;

public Treatment() { }

@SequenceGenerator( // It only takes effect for
        name = "TreatmentIdGenerator", // databases providing identifier
        sequenceName = "TreatmentSeq")// generators.
@Id
@GeneratedValue(strategy = GenerationType.AUTO, generator = "TreatmentIdGenerator")
public Long getTreatmentId() {
    return treatmentId;
}

public void setTreatmentId(Long treatmentId) {
    this.treatmentId = treatmentId;
}

@ManyToOne(optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "patient")
public UserAccount getPatient() {
    return patient;
}

public void setPatient(UserAccount patient) {
    this.patient = patient;
}

@ManyToOne(optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "regimen")
public Regimen getRegimen() {
    return regimen;
}

public void setRegimen(Regimen regimen) {
    this.regimen = regimen;
}

@ManyToOne(optional = false, fetch = FetchType.LAZY)
@JoinColumn(name = "medicine")
public Medicine getMedicine() {
    return medicine;
}

public void setMedicine(Medicine medicine) {
    this.medicine = medicine;
}

}

Concretely, I need to get a Regimen property. So put next annotation in the class is not valid for me:

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

I tried to add OpenSessionInViewFilter to my WebApplicationInitializer like this:

public class DiaryInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

@Override
public void onStartup(ServletContext servletContext) throws ServletException {

    OpenSessionInViewFilter openSessionInView = new OpenSessionInViewFilter();
    openSessionInView.setSessionFactoryBeanName("sessionFactory");

    servletContext.addFilter("openSessionInView", openSessionInView)
            .addMappingForUrlPatterns(null, false, "/*");

    // add other filters like this
    super.onStartup(servletContext);
}

@Override
protected Class<?>[] getRootConfigClasses() {
    return new Class[] { DiaryConfiguration.class };
}

@Override
protected Class<?>[] getServletConfigClasses() {
    return null;
}

@Override
protected String[] getServletMappings() {
    return new String[] { "/" };
}

}

And this is my jackson dependency:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.5.3</version>
</dependency>

These are the errors I'm getting:

com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: es.udc.fic.tfg.mferreiro.diary.model.treatmentservice.TreatmentDetails[&quot;regimen&quot;]-&gt;es.udc.fic.tfg.mferreiro.diary.model.regimen.Regimen_$$_jvstbae_3[&quot;handler&quot;])
com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:59)
com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:26)
com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:575)
com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:666)
com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:156)
com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:575)
com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:666)
com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:156)
com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:129)
com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:851)
org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:264)
org.springframework.http.converter.AbstractGenericHttpMessageConverter.write(AbstractGenericHttpMessageConverter.java:100)
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:222)
org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor.handleReturnValue(HttpEntityMethodProcessor.java:183)
org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:80)
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:126)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:814)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:737)
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:969)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:860)
javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:845)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
org.springframework.orm.hibernate4.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:151)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)

Note: I'm using Hibernate 4 and Spring MVC 4

Any help will be appreciated, thanks in advance!

Upvotes: 1

Views: 2749

Answers (1)

meriton
meriton

Reputation: 70564

Background

When hibernate loads an entity with a lazy non-null relationship from the database, it will (usually) create a lazy loading proxy for the referred entity, and put that proxy object into the corresponding property of the object being loaded. This proxy object will extend the class or interface of the referred entity, and any invocation of methods from that class or interface will cause the proxy object to initialize, which fetches the data from the database (this only works while the session is still open, otherwise initialization will throw an exception), and returns the loaded data.

Your OpenSessionInViewFilter keeps the session open during the entire request processing, so if somebody would invoke a getter on the proxy object, the proxy object would initialize successfully rather than throwing a LazyInitializationException.

Your JsonProcessingException however happens before that. It indicates that Jackson does not know in what form it should serialize such a proxy object.

Solution

The standard solution is using jackson-datatype-hibernate. This module also allows you to configure whether Jackson should initialize proxies (i.e. fetch the state from the database) or just serialize the identity of the referred entity instead. The latter allows you to flexibly control how much data should be written to the response (by only initializing the relationships the client needs), and does not require an OpenSessionInViewFilter (because the proxies are initialized early, or not at all).

Upvotes: 5

Related Questions