What is described below works in Spring Boot 2/Hibernate 5 but stopped working when migrating to Hibernate 6/Spring boot 3.
I have an Hibernate managed entity with a collection attribute (List or Map, doesn't really matter), the value is converted from the DB using an AttributeConverter.
Now the problem: when the value is updated, this is not anymore detected as a change and at the end of the transaction, the entity is not updated (assuming only this attribute changed). The collection attribute is not marked dirty anymore.
(Code has been reduced to serve as illustration only)
@Entity
@Table(name = "person")
public class Person {
@Id
private int id;
...
@Convert(converter = HashMapConverter.class)
private Map<String, Object> attributes = new HashMap<>();
...
}
public class HashMapConverter implements AttributeConverter<HashMap<String, Object>, String> {
private final ObjectMapper mapper = new ObjectMapper();
@Override
public String convertToDatabaseColumn(HashMap<String, Object> map) {
try {
return mapper.writeValueAsString(map);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
@Override
public HashMap<String, Object> convertToEntityAttribute(String string) {
try {
return mapper.readValue(string, new TypeReference<>() {});
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
I did some digging, the CollectionJavaType in Hibernate is mutable, the Entity is mutable, but it seems the BasicAttributeMapping for the collection attribute flags the attribute as Immutable (ImmutableMutability), so the EntityInitialiser makes no copy or a shallow copy. That seems to be causing the changes to go undetected.
If I add a specific MutabilityPlan to the Converter, marking the Converter mutable, the change is correctly detected, attribute is marked dirty and entity is updated in the DB.
@Mutability(MyPlan.class)
...
class MyPlan extends MutableMutabilityPlan<HashMap<String, Object>> {
@Override
protected HashMap<String, Money> deepCopyNotNull(HashMap<String, Object> value) {
return new HashMap(value);
}
}
But this brings a new problem: the DirtyCheck through the CollectionJavaType now always flags the attribute as dirty, even if no changes are made.
Should the original code still work in Hibernate 6? Or do I really need to add a specific MutabilityPlan for every Converter I have?
CodePudding user response:
Sounds like you ran into this issue
A way to test this would be to convert to
@Override
public Map<String, Object> convertToEntityAttribute(String string) {
try {
return new HashMap(mapper.readValue(string, new TypeReference<>() {}));