please analize the following two codes and tell me why the first one fails with primary key violation when doing commit, and the second one does’t.
Code which fails at commit:
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");
}
Code which works fine:
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");
}
The only difference is that the first one does not commit the delete query, but instead commit it all together at the end.
Cliente and Puntaje is a 1:N bidirectional relation with cascade = ALL.
And all the inserted instances of Cliente have the same ID, but merge should be smart enough to update instead of insert after the first one is persisted, but that seems to fail at the first example and i cant find any explanation.
Also im using H2 embedded database.
Also i would like to add, the first code works FINE if there is an already inserted Cliente value, this fails when the table is actually empty and so delete actually is doing nothing.
This is the error im getting:
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
)
These are the tables:
@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;
When you perform operations on the object, all the operations are recorded in the cache ONLY. JPA will prepare an internal list of all objects to be inserted, updated and deleted. Which will be flushed together when
flushorcommitis called.Now take your first example. You deleted all
Puntaje, which adds allPuntajein thedeletedlist. Now when you callmerge, i*ts indeed smart enough* and it figured out that it should be inserted and not updated and added in theinsertlist. When you call commit, it tries to insert the objects from the insert list first and as you can expect, it will fail as old objects are not yet deleted.Only difference in your second example is that, by force, you are deleting the objects first before insertion and hence it’s not failing.
I am sure, it will not fail even if your use
flushin place ofcommit.Hope this helps you understand the reasoning behind the failure.