use*_*038 5 java hibernate jpa
在合并我的 Hibernate 实体之一时,我观察到了约束违规。有两个表 A 和 B,其中一个保存元数据,另一个是字符串值的映射。
在表 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
Run Code Online (Sandbox Code Playgroud)
现在,当我合并 A (id = 23) 时,将为fooKey和执行 UPDATE 语句barKey。然而,对于errorKey,Hibernate 将改为发出 INSERT 语句并导致约束违规。
所以我的问题是:
null?表 A( id、版本、最后更新)
表 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...
}
Run Code Online (Sandbox Code Playgroud)
答案 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();
Run Code Online (Sandbox Code Playgroud)
这是初始持久化过程中发生的情况:
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', '')
Run Code Online (Sandbox Code Playgroud)
Oracle 是如此“聪明”(愚蠢?)以至于它插入 NULL 而不是空字符串。所以在db中B表中有(1, 'b', NULL)记录:
select * from b;
ID KEY VALUE
1 b (null)
Run Code Online (Sandbox Code Playgroud)
现在进行查找:
A found = em.find(A.class, 1l);
System.out.println("found no. 1: " + found);
Run Code Online (Sandbox Code Playgroud)
请注意,输出清楚地表明 Hibarnate 尚未选取 NULL 值元素:
found no. 1: id: 1, version: 1, mapping: {}
Run Code Online (Sandbox Code Playgroud)
现在是与“更新”值的合并——假设更新值是“任何内容”。请注意,这实际上是一个补充:
found.myMapping.put("b", "anything");
em.getTransaction().begin();
em.merge(found);
em.getTransaction().commit();
em.clear();
Run Code Online (Sandbox Code Playgroud)
由于这是一个补充,这就是 Hibernate 将尝试做的事情:
insert into B (ID, KEY, VALUE) values (1, 'b', 'anything')
Run Code Online (Sandbox Code Playgroud)
并且插入失败,并显示 ORA-00001:违反了唯一约束(XXX.SYS_C0045198):B 中确实存在具有此主键(1,'b')的记录。
关于此的另一篇好文章:
http://koenseneels.blogspot.hu/2012/09/hibernates-map-behaviour.html