Hibernate and Jackson lazy serialization
Asked Answered
P

2

5

I'm working on a project using Hibernate and Jackson to serialize my objects. I think I understand how it is suposed to work but I can't manage to make it works.

If I understand well, as soon as a relation fetch mode is set to LAZY, if you want this relation, you have to initialize it.

Here is my class :

@Entity
@JsonIgnoreProperties(ignoreUnknown = true)
@Table(schema="MDDI_ADMIN", name = "MINIUSINE")
@Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
public class MiniUsine {

    @Id
    @Column(name="MINIUSINEID", nullable = false)
    private int miniUsineID;

    @Column(name = "NAME", length = 40, nullable = false)
    private String name;

    @OneToMany(cascade = CascadeType.ALL)
    @JoinColumn(name="FluxID")
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    private Set<Flux> fluxs = new HashSet<Flux>();

And all getters and setters.

I've also tried this JsonInclude.Include.NON_EMPTY as class annotation. Also tried the NON_NULL. However, jackson keeps sending me

com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: MiniUsine.fluxs, no session or session was closed (through reference chain: java.util.ArrayList[0]->MiniUsine["fluxs"])

I'm serializing it with : mapper.writeValueAsString(optMU);

Using Jackson 2.3.2

Thanks for help

Professoriate answered 7/6, 2016 at 12:6 Comment(0)
L
3

As far as I understand, the entity object that hibernate returns is a proxy which derives from your entity class. If you try to access getter methods for lazy fields outside of a transaction, you get LazyInitializationException. The point I want to make is setting fluxs to empty set doesn't help you at all.

private Set<Flux> fluxs = new HashSet<Flux>();

Hibernate overloads the getter and if you try to access it outside of a transaction(which jackson is doing to check if it is empty), you get the LazyInit error.

Latoshalatouche answered 7/6, 2016 at 12:19 Comment(9)
But it is supposed to not access it when it is empty (when I don't initialize it) because of @JsonInclude(JsonInclude.Include.NON_EMPTY) I never access it by myself, only jackson wants to access it when serializing the objectProfessoriate
@JsonInclude(JsonInclude.Include.NON_EMPTY) means include this field only if it's non-empty. But to check whether its empty, Jackson needs to call .isEmpty() on the collection? However that collection is not the empty hash_set that you have provided. It is provided by hibernate. And that call will happen outside a transaction resulting in the error.Latoshalatouche
Since the fetchmode is LAZY, the collection isn't supposed to be loaded if I don't initialize it, so it should be empty when jackson check it :(Professoriate
#25341106 read the accepted answer. I hope it clears your doubts about how hibernate works.Latoshalatouche
Ok I guess I understand, Hibernate tries to generate the collection when Jackson checks if it is empty or no but since session is close, it crashes. Is there anyway to avoid it so ? :/Professoriate
You can initialize the relation by calling entity.getFluxs().size()Latoshalatouche
Yea but I would like to not initialize it because in THIS case, I won't need this collection and initializing it causes a big performance loss (since Flux will have another relation to something which will have again another relation to something else etc..)Professoriate
You can try using @JsonIgnore to ignore it completely. Or if you need it in some cases you have to initialise by calling entity.getFluxs().size().Latoshalatouche
Ok si there is no way to generate it sometimes and avoid its at other time to boost performances, thanks for all explanations!Professoriate
D
5

I know this is an old question but I had the same problem.

You must add a new maven dependecy to support JSON serialization and deserialization of Hibernate. I used Hibernate5 so I added

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-hibernate5</artifactId>
    <version>2.9.2</version>
</dependency>

Now register the new module.

@Provider
public class JacksonHibernateProvider implements ContextResolver<ObjectMapper> {

    @Override
    public ObjectMapper getContext(final Class<?> type) {
        final ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new Hibernate5Module());
        return mapper;
    }
}
Diehl answered 22/11, 2017 at 11:33 Comment(0)
L
3

As far as I understand, the entity object that hibernate returns is a proxy which derives from your entity class. If you try to access getter methods for lazy fields outside of a transaction, you get LazyInitializationException. The point I want to make is setting fluxs to empty set doesn't help you at all.

private Set<Flux> fluxs = new HashSet<Flux>();

Hibernate overloads the getter and if you try to access it outside of a transaction(which jackson is doing to check if it is empty), you get the LazyInit error.

Latoshalatouche answered 7/6, 2016 at 12:19 Comment(9)
But it is supposed to not access it when it is empty (when I don't initialize it) because of @JsonInclude(JsonInclude.Include.NON_EMPTY) I never access it by myself, only jackson wants to access it when serializing the objectProfessoriate
@JsonInclude(JsonInclude.Include.NON_EMPTY) means include this field only if it's non-empty. But to check whether its empty, Jackson needs to call .isEmpty() on the collection? However that collection is not the empty hash_set that you have provided. It is provided by hibernate. And that call will happen outside a transaction resulting in the error.Latoshalatouche
Since the fetchmode is LAZY, the collection isn't supposed to be loaded if I don't initialize it, so it should be empty when jackson check it :(Professoriate
#25341106 read the accepted answer. I hope it clears your doubts about how hibernate works.Latoshalatouche
Ok I guess I understand, Hibernate tries to generate the collection when Jackson checks if it is empty or no but since session is close, it crashes. Is there anyway to avoid it so ? :/Professoriate
You can initialize the relation by calling entity.getFluxs().size()Latoshalatouche
Yea but I would like to not initialize it because in THIS case, I won't need this collection and initializing it causes a big performance loss (since Flux will have another relation to something which will have again another relation to something else etc..)Professoriate
You can try using @JsonIgnore to ignore it completely. Or if you need it in some cases you have to initialise by calling entity.getFluxs().size().Latoshalatouche
Ok si there is no way to generate it sometimes and avoid its at other time to boost performances, thanks for all explanations!Professoriate

© 2022 - 2024 — McMap. All rights reserved.