Reputation: 183
I have 2 simple classes:
Cat
CatStore
Cat
has nothing particular, and CatStore
has a List<Cat>
, the relation mapped as:
In Cat
class:
@ManyToOne
private CatStore catstore;
In CatStore
:
@OneToMany(mappedBy="catstore", fetch=FetchType.EAGER)
private List<Cat> cats;
The relation seems to work OK, as Jackson can generate me a correct JSON when I run
String json = mapper.writeValueAsString(catStore);
But if I try to deserialize the JSON I just generated, with this line:
CatStore c2 = mapper.readValue(json, CatStore.class);
I get a JsonProcessingException
saying
failed to lazily initialize a collection, could not initialize proxy - no Session (through reference chain: fr.truc.java.cleanspringbootMVC.model.CatStore["cats"]
The deserialization actually works if there is no cats in the CatStore
like with the following JSON:
{
"id":123,
"cats":["org.hibernate.collection.internal.PersistentBag",[]],
"name":"NO cats"
}
But this one triggers the error:
{
"id": 121,
"cats": ["org.hibernate.collection.internal.PersistentBag", [
["fr.truc.java.cleanspringbootMVC.model.Cat", {
"id": 118,
"name": "luc",
"nbPattes": 4
}],
["fr.truc.java.cleanspringbootMVC.model.Cat", {
"id": 119,
"name": "andré",
"nbPattes": 4
}],
["fr.truc.java.cleanspringbootMVC.model.Cat", {
"id": 120,
"name": "cheval",
"nbPattes": 4
}]
]],
"name": "Cat en kit"
}
I really do not know why a lazy loading exception would trigger here as the data is contained in the JSON string.
Is this related to the PersistentBag?
Is this because I do not use a custom serializer/deserializer?
Upvotes: 0
Views: 327
Reputation: 1
I ran into exactly the same issue. To fix it while using enableDefaultTyping
(or the currently recommended activateDefaultTyping
) I had to use Hibernate5Module: (Note that the required module depends on your hibernate version and has to be added as an additional dependency.)
mapper.registerModule(new Hibernate5Module()
.configure(Hibernate5Module.Feature.REPLACE_PERSISTENT_COLLECTIONS, true));
This replaces the PersistentBag
in your output with an ArrayList
and prevents the deserialization error.
However this feature does not seem to work with FetchType.EAGER
for a reason unknown to me. Fortunately @Fetch(FetchMode.JOIN)
does the trick.
Another thing to take into account is that the REPLACE_PERSISTENT_COLLECTIONS
feature generated malformed Json for me (wrapping the list content in double square brackets). But this was resolved by using lenient deserialization.
Upvotes: 0
Reputation: 17874
If you use JPA, all your entities will be enhanced by bytecode to allow lazy loading and such. They are not really suitable for serialization, except by hibernate, who is aware of it. But Jackson doesn't know about it, so it will screw up the enhanced bytecode.
It's also not a good idea to use the same class for JPA mapping and for Jackson mapping. The JPA classes have fields that map to columns and such, whereas the classes for Jackson map to a javascript schema. If you use them in the same class, you can never change your database schema without affecting your JSON schema and possible breaking backwards compatibility with whoever is using the JSON.
It's better to have separate classes (DTO's) to use for Jackson, and do some mapping between them. Or use mapstruct to generate a mapper automatically.
Upvotes: 1
Reputation: 183
The error seems to be related to mapper.enableDefaultTyping();
When enabled, the typing of each class is outputted in the JSON. (which I wanted)
For some reason, it causes the above error on a @ManyToOne
When disabled, PersistentBag no longer appears in my serialized JSON, and the deserializing works...
Upvotes: 0