由于JPA AttributeConverter丢失数据? [英] Data lost because of JPA AttributeConverter?

查看:137
本文介绍了由于JPA AttributeConverter丢失数据?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用JPA将数据保存到数据库。详细我使用Hibernate 4.3.5作为JPA实现。
由于性能和保持表格结构的简单性,我并不直接将1:1从对象映射到表格,而是仅将一些数据列表保留为对象,但不创建实体类别。相反,我将对象结构作为JSON序列化到数据库。这个序列化/反序列化是用@Converter完成的,并且工作起来非常好。



简化代码:

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

私人字符串名称;

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

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

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

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

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

...
}

据了解,这主要是在工作!
我在单元测试中发现了以下问题:

  //用列表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();

//将订单更改为A,C,B:
getEM()。clear();
EntitySample loaded = getEM()。find(...); //只是从DB
重新加载SampleObject moveObj = loaded.getSampleList()。remove(1);
loaded.getSampleList()。add(moveObj);
// loaded.setName(changed); //所有这些变化都适用,但并非没有!

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

使用上面的代码,带有元素A,B,C的JSON列表的对象被写入D B。在此之后,对象再次被加载并且列表元素的顺序被改变为A,C,B。但是现在保存该对象不会改变数据库中的数据数据!对我来说,看起来,Hibernate没有检测到任何东西已经改变!我的转换器代码convertToDatabaseColumn()不被称为合并。
,但只要我也改变了名字(上面的注释行),所有的工作正常。现在加载的对象似乎被检测为已更改,因此也会调用该转换,并将JSON字符串存储到数据库。



有人知道这个错误或知道解决方法?或者它到底是我的错误?

解决方案

我知道这是一个老问题,但...

b
$ b

我在Hibernate + @ Converter中遇到过同样的问题。
过了一段时间,我意识到这是因为Hibernate不知道什么时候 AttributeConverter< left,right> 的左侧变脏了(可能有其他一些JPA实现),这就是为什么它从不调用 convertToDatabaseColumn()并因此从不更新数据库。



要解决此问题,你必须将实体的属性设置为一个新的实例( clone()可以做到这一点。)

  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();

//将订单更改为A,C,B:
getEM()。clear();
startTransaction(); //< - 事务应该从这里开始
EntitySample loaded = getEM()。find(...);
List< SampleObject> list = loaded.getSampleList();
SampleObject moveObj = list.remove(1);
list.add(moveObj);
loaded.setSampleList(list.clone()); //< - 解决方法

// getEM()。merge(loaded); //< - '已加载'已经是一个实体!
commitTransaction();

这里 clone()返回一个浅拷贝 List 实例。 ( SampleObject 本身的实例不会被复制。)

为了避免这种情况,您必须创建一个新的Hibernate类型(抛开纯JPA),但这与主要问题无关。


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?

解决方案

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.

这篇关于由于JPA AttributeConverter丢失数据?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆