在合并我的一个Hibernate实体时,我发现了一个约束违规。有两个表A和B,其中一个包含元数据,另一个是String值的映射。
在表B中,(id,key)上有一个组合主键。
现在,当我合并A的现有实例(也有B的几个条目)时,Hibernate将为B执行UPDATE和INSERT语句。
UPDATE语句没有问题,但是无法执行INSERT语句,因为它会导致约束违规,因为(id,key)是唯一的组合。
我发现只有在B中的现有值为null
时才会出现此问题。
我在Oracle 11,JPA 2.1和EJB 3.2上。但不完全确定,如何确定Hibernate版本。
让我们假设这些是表B的条目:
23, fooKey, foo
23, barKey, bar
23, errorKey, null
现在,当我合并A(id = 23)时,将对fooKey
和barKey
执行UPDATE语句。但是,对于errorKey
,Hibernate将发出一个INSERT语句,并导致约束违规。
所以我的问题是:
null
?表A( id,版本,lastUpdate )
表B( ID,键,值)
@Entity
public class A {
@Id
public long id;
public long version = 0;
public Date lastUpdate = new Date();
@ElementCollection(fetch = FetchType.EAGER)
@CollectionTable(name = "B", joinColumns = @JoinColumn(name = "ID"))
@MapKeyColumn(name = "KEY")
@Column(name = "VALUE", length = 2000)
public Map<String, String> myMapping = new HashMap<String, String>();
// Setters and getters...
}
答案 0 :(得分:1)
答案1:从Hibernate的角度来看,集合中的NULL值元素不存在。因此,当您将元素“更新”为非空值时,Hibernate会执行INSERT语句。
答案2:在使用Hibernate时,你应该避免集合中的空值(或包装它们),并且在使用Oracle时应避免使用空格(见下文)。
我用Oracle重现了你的问题。首先,我存储一个“”(空字符串)值元素,然后将其更新为任何其他字符串。我使用log4jdbc来检查Hibernate发出的实际语句。
这是演示(使用Oracle失败,H2可以工作):
最初的坚持:
A a = new A();
a.setId(1);
a.setLastUpdate(new Date());
a.setVersion(1);
Map<String, String> myMap = new HashMap<>();
myMap.put("b", "");
a.setMyMapping(myMap);
em.getTransaction().begin();
em.persist(a);
em.getTransaction().commit();
em.clear();
这是在初始持续期间发生的事情:
insert into A (lastUpdate, version, id) values (to_timestamp('07/13/2017 12:28:37.112', 'mm/dd/yyyy hh24:mi:ss.ff3'), 1, 1)
insert into B (ID, KEY, VALUE) values (1, 'b', '')
Oracle是如此“聪明”(愚蠢?)它插入NULL而不是空字符串。所以在db中,B表中有(1,'b',NULL)记录:
select * from b;
ID KEY VALUE
1 b (null)
现在来了查找:
A found = em.find(A.class, 1l);
System.out.println("found no. 1: " + found);
请注意,输出清楚地表明Hibarnate没有拾取NULL值元素:
found no. 1: id: 1, version: 1, mapping: {}
现在合并了“更新”值 - 让我们说更新的值是“任何东西”。请注意,它实际上是一个补充:
found.myMapping.put("b", "anything");
em.getTransaction().begin();
em.merge(found);
em.getTransaction().commit();
em.clear();
由于这是一个补充,这就是Hibernate将尝试做的事情:
insert into B (ID, KEY, VALUE) values (1, 'b', 'anything')
插入失败并且ORA-00001:违反了唯一约束(XXX.SYS_C0045198):B中确实存在一个带有此主键(1,'b')的记录。
另一篇关于此的好文章:
http://koenserneels.blogspot.hu/2012/09/hibernates-map-behaviour.html