请分析以下两个代码并告诉我为什么第一个代码在执行提交时因主键违规而失败,而第二个代码确实没有。
在提交时失败的代码:
try{
Query q = em.createQuery("DELETE FROM Puntaje");
q.executeUpdate();
//em.getTransaction().commit();
//em.getTransaction().begin();
Iterator it = l.iterator();
while(it.hasNext()){
DataPuntaje dp = (DataPuntaje)it.next();
Cliente c = new Cliente(dp.getCliente());
Puntaje p = new Puntaje(dp.getPuntaje(),c);
c.agregarPuntaje(p);
em.merge(c);
}
System.out.println("test1");
em.getTransaction().commit();
System.out.println("test2");
}
运作良好的代码:
try{
Query q = em.createQuery("DELETE FROM Puntaje");
q.executeUpdate();
em.getTransaction().commit();
em.getTransaction().begin();
Iterator it = l.iterator();
while(it.hasNext()){
DataPuntaje dp = (DataPuntaje)it.next();
Cliente c = new Cliente(dp.getCliente());
Puntaje p = new Puntaje(dp.getPuntaje(),c);
c.agregarPuntaje(p);
em.merge(c);
}
System.out.println("test1");
em.getTransaction().commit();
System.out.println("test2");
}
唯一的区别是第一个不提交删除查询,而是在最后将它们全部一起提交。 Cliente和Puntaje是1:N双向关系,cascade = ALL。 并且所有插入的Cliente实例都具有相同的ID,但是合并应该足够智能,以便在第一个持久化之后更新而不是插入,但是在第一个示例中似乎失败并且我无法找到任何解释。 我也使用H2嵌入式数据库。
另外我想补充一点,如果已经插入了Cliente值,第一个代码就会运行FINE,当表实际为空时会失败,因此删除实际上什么都不做。
这是我得到的错误:
Internal Exception: org.h2.jdbc.JdbcSQLException: Unique index or primary key violation: "PRIMARY_KEY_5 ON PUBLIC.CLIENTE(NICK)"; SQL statement:
INSERT INTO CLIENTE (NICK) VALUES (?) [23505-169]
Error Code: 23505
Call: INSERT INTO CLIENTE (NICK) VALUES (?)
bind => [cbaldes]
Query: InsertObjectQuery(Clases.Cliente@21cd5b08)
javax.persistence.RollbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.0.2.v20100323-r6872): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.h2.jdbc.JdbcSQLException: Unique index or primary key violation: "PRIMARY_KEY_5 ON PUBLIC.CLIENTE(NICK)"; SQL statement:
INSERT INTO CLIENTE (NICK) VALUES (?) [23505-169]
Error Code: 23505
Call: INSERT INTO CLIENTE (NICK) VALUES (?)
bind => [cbaldes]
Query: InsertObjectQuery(Clases.Cliente@21cd5b08
)
这些是表格:
@Entity
public class Puntaje implements Comparable, Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private int total;
@ManyToOne(cascade=CascadeType.ALL, optional = false)
@JoinColumn(name="NICK")
private Cliente cliente;
@Entity
public class Cliente implements Serializable {
@Id
private String nick;
@OneToMany(cascade=CascadeType.ALL, mappedBy="cliente")
private List<Puntaje> puntajes;
答案 0 :(得分:2)
对对象执行操作时,所有操作都只记录在缓存中。 JPA将准备一个包含要插入,更新和删除的所有对象的内部列表。在调用flush
或commit
时,这些对象将被刷新。
现在来看你的第一个例子。您删除了所有Puntaje
,这会在Puntaje
列表中添加所有deleted
。现在,当您致电merge
时,我确实足够智能 *并且它发现它应该被插入而不会更新并添加到insert
列表中。当您调用commit时,它会尝试首先插入插入列表中的对象,如您所料,它将失败,因为旧对象尚未删除。
只有第二个例子的不同之处在于,通过强制,您在插入之前首先删除对象,因此它不会失败。
我相信,即使您使用flush
代替commit
,也不会失败。
希望这有助于您了解失败背后的原因。