Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Data lost because of JPA AttributeConverter?

I use JPA to persist my data to the database. In detail I use Hibernate 4.3.5 as JPA implementation. Because of performance and keeping the table structure simple, I don not map directly 1:1 from objects to tables but some list of data I only keep as objects but do not create entity classes for. Instead of this, I serialize the object structure as JSON to the DB. This serialization/deserialization is done with @Converter and works mostly nice.

Simplified Code:

@Entity
public class EntitySample {
  ...
  @Convert(converter=ConverterSample.class)
  private List<SampleObject> sampleList=new ArrayList<>();

  private String name;

  public List<SampleObject> getSampleList() {
    return sampleList;
  }

  public void setName(String newName) {
    name=newName;
  }
  ...
}

@Converter
public class ConverterSample implements AttributeConverter<List,String> {

  @Override
  public String convertToDatabaseColumn(List data) {
    return serializeToJSON(data);
  }

  @Override
  public List convertToEntityAttribute(String data) {
    return deserializeFromJSON(data);
  }

  ...
}

As told, it is mostly working! I detected the following problem in a unit test:

// create a new entity object with list A, B, C:
EntitySample entity=new EntitySample();
entity.getSampleList().add(new SampleObject("A"));
entity.getSampleList().add(new SampleObject("B"));
entity.getSampleList().add(new SampleObject("C"));
entity.setName("init");
startTransaction();
getEM().persist(entity);
commitTransaction();

// change the order to A, C, B:
getEM().clear();
EntitySample loaded=getEM().find(...); // just reload from DB
SampleObject moveObj=loaded.getSampleList().remove(1);
loaded.getSampleList().add(moveObj);
// loaded.setName("changed"); // all works with this change, but not without!

startTransaction();
getEM().merge(loaded);
commitTransaction();

With the upper code, an object with a JSON list with elements A, B, C is written to DB. After this the object is loaded back again and the order of the list elements is changed to A, C, B. But now saving the object does NOT change data data in the database! For me it looks like, that Hibernate does not detect that anything has changed! My converter code convertToDatabaseColumn() is not called for the merge. but as soon as I also change the name (commented line above), all works fine. Now the loaded object seems to be detected as changed and therefore also the conversion is called and the JSON string is stored to DB.

Does anybody know this bug or knows a workaround? Or is it my bug in the end?

like image 877
Martin Metz Avatar asked Oct 31 '22 11:10

Martin Metz


1 Answers

I know it's an old question, but...

I've had the same problem with Hibernate+@Converter once. After a while, I realized that's because Hibernate doesn't know when the left side of AttributeConverter<left,right> gets dirty (maybe some other JPA implementations do), that's why it never calls convertToDatabaseColumn() and therefore never updates the database.

To work around this issue, you must set the entity's property to a fresh new instance (a clone() would do the trick).

EntitySample entity=new EntitySample();
entity.getSampleList().add(new SampleObject("A"));
entity.getSampleList().add(new SampleObject("B"));
entity.getSampleList().add(new SampleObject("C"));
entity.setName("init");
startTransaction();
getEM().persist(entity);
commitTransaction();

// change the order to A, C, B:
getEM().clear();
startTransaction();  // <-- Transaction should start here
EntitySample loaded=getEM().find(...);
List<SampleObject> list = loaded.getSampleList();
SampleObject moveObj = list.remove(1);
list.add(moveObj);
loaded.setSampleList(list.clone()); // <-- Workaround

// getEM().merge(loaded); // <-- 'loaded' already is an entity!
commitTransaction();

Here clone() returns a shallow copy of the List instance. (The instances of SampleObject themselves are not copied.)

To avoid this you have to create a new Hibernate Type (leaving pure JPA aside), but that's not related to the main question.

like image 76
Wilfredo Pomier Avatar answered Nov 13 '22 14:11

Wilfredo Pomier