0

I am new to JPA and was experimenting a bit in hope of figuring out the Entity lifecycle model. I expected the code below to throw an EntityExistsException. However, instead of throwing the expected exception, it just creates a new row in database table with a new primary key (incremented by one).

Can anyone explain this? I suspect it might have something to do with the @GeneratedValue annotation on my ID field in combination with my autoincrementing primary key in the corresponding table.

Below you can find 3 snippets: the code I try to run, my entity definition and my table design.

I would like to thank you in advance for shining your light on my misunderstanding.

Kind regards

ps: I use eclipselink as jpa implementation and a Derby database

Script:

    Teacher teacher = new Teacher("John","Doe","English");

    //Persist John Doe
    EntityManager em = Persistence.createEntityManagerFactory("jpatest").createEntityManager(); 
    em.getTransaction().begin();
    em.persist(teacher);
    em.getTransaction().commit();
    em.close();

    //Try to persist John Doe a second time
    em = Persistence.createEntityManagerFactory("jpatest").createEntityManager();
    em.getTransaction().begin();
    em.persist(teacher);
    em.getTransaction().commit(); //I Expect a throw here
    em.close();

Table Design:

CREATE TABLE teachers (id INT GENERATED ALWAYS AS IDENTITY, firstname VARCHAR(20) ,lastname VARCHAR(40), PRIMARY KEY(id))

Entity definition:

package testpackage;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;

@Entity
@Table(name="teachers", schema="USERNAME")
public class Teacher {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    private String firstName;

    private String lastName;

    @Transient
    private String course;




    /**
     * Zero argument constructor for JPA
     */
    Teacher(){
    }

    public Teacher(String firstName, String lastName, String course){
        this.firstName = firstName;
        this.lastName = lastName;
        this.course = course;
    }




    public int getId(){
        return id;
    }
    public String getFirstName(){
        return firstName;
    }
    public String getLastname(){
        return lastName;
    }
    public String getCourse(){
        return course;
    }



    public void setFirstName(String firstName){
        this.firstName = firstName;
    }
    public void setLastName(String lastName){
        this.lastName = lastName;
    }
}
Skifozoa
  • 303
  • 2
  • 13
  • This looks like an eclipselink bug to me. EclipseLink should consider the entity as a detached one, and the spec says that an exception must be thrown if a detached entity is passed to persist(). – JB Nizet Apr 22 '14 at 11:47
  • The problem will be that the teacher instance does not have an id assigned after the first commit. Without the id, a second persist call should succeed, as it has no identity and so must be new. Turn on logging to all or Finest and see if there is any indication of a problem in the logs, such as the wrong database platform being used, or EclipseLink not issuing the insert correctly. What values are used in the database for the id? Does it work if you switch to using Integer instead of int, or a different generation strategy? – Chris Apr 22 '14 at 12:39

2 Answers2

1

EntityExistsException will be thrown only when you are persisting the same entity associated with DB.

but here you have created one Object with some values and then persisting 2 times with primary key generated automatically unique for each persistence.

because

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)

will generate new ID each time you call persist method with Entity manager. and henc eboth the entities becomes different with respect to DB is concerned. because DB cares for primary key to be unque to distinguish each record.

so better you do this to get that exception

get the entity from DB first and change it and try to call save on it

EntityManager em = Persistence.createEntityManagerFactory("jpatest").createEntityManager(); 
    em.getTransaction().begin();
Teacher teacher = em.find(Teacher.class,primary key)
    em.persist(teacher);
    em.getTransaction().commit();
    em.close();

or else remove autogeneration of primary key and assign it manually same id both the time. let me know for any other questions

Karibasappa G C
  • 2,686
  • 1
  • 18
  • 27
0

It is clear form exception the entity already exists, if already exists the EntityExistsException may be thrown when the persist operation is invoked.

EclipseLink does not include the Id in the INSERT for Derby IDENTITY, so you have something odd going on.

Did you previously use another generator strategy and not recompile/deploy your code correctly?

Also try setting your platform using the"eclipselink.target-database"="Derby" in your persistence.xml.

make little change

id INT GENERATED ALWAYS AS IDENTITY

use START WITH 1, INCREMENT BY 1 for PK.

id INT GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1)
Asif Bhutto
  • 3,916
  • 1
  • 24
  • 21