Rahul Shivsharan
Rahul Shivsharan

Reputation: 2559

Reusing Persistence Context in JPA

i was just working on @EmbededId code, i want to do an auto increment before the entity is persisted, this is want to do without use of @GeneratedValue and an identity column,

below is the table with composite id,

create table TBL_EMPLOYEE_002(
    ID integer, 
    COUNTRY varchar(50),
    NAME varchar(50),
    constraint PK_EMP_00240 primary key(ID,COUNTRY)
)

this is the code for Entity mapping,

@Entity
@Table(name="TBL_EMPLOYEE_002")
public class EmployeeEntitySix implements Serializable{

// contructor's

    @EmbeddedId    
    private EmployeeIdTwo id;

    @Column(name="NAME")
    private String employeeName;

// getters and setter's

    @PrePersist
    public void incId(){
        EntityManager em = null;
        Query q = null;
       EntityManagerFactory emf = null;
        try{
            emf = Persistence.createEntityManagerFactory("forPractise");
            em = emf.createEntityManager();
            q = em.createQuery("select max(e.id.employeeId) from EmployeeEntitySix e");
            List list = q.getResultList();
            Integer i = (list != null && list.size() > 0) ? Integer.valueOf(list.get(0).toString()) : 0;
            this.getId().setEmployeeId(++i);
        }catch(Exception e){
            System.out.println("EXCETION WHILE INCREASING COUNTER...");
            e.printStackTrace();
        }finally{
            if(em != null && em.isOpen()){
                em.close();
            }
            if(getEmf() != null && getEmf().isOpen()){
                getEmf().close();
            }
        }


}

This is the composite id mapping,

@Embeddable
public class EmployeeIdTwo implements Serializable{


    @Column(name="ID") 
    private Integer employeeId;

    @Column(name="COUNTRY",length=50)
    private String empCountry;

// getters and setters

    }

this code is of my main method, this main method is in some other class,

public static void main(String [] args){
    EntityManagerFactory emf = null;
    EntityManager em = null;
    EntityTransaction tx = null;
    try{
        emf = Persistence.createEntityManagerFactory("forPractise");
        em = emf.createEntityManager();
        tx = em.getTransaction();            
        tx.begin();
    EmployeeEntitySix employee = new EmployeeEntitySix(new EmployeeIdTwo("ZIMBABWE"), "Henry Olanga");
    em.persist(employee);
....
}

Now the above code runs fine, whenever i persist the entity "EmployeeEntitySix", the method annotated with @PerPersist runs, which will first fetch the max id, increments its, set it into the id in the embeded entity and persist the entity.

Now my question is,

I am creating EntityManagerFactory twice, first in the main method, second time in the @PrePersist method in entity EmployeeEntitySix. So whether i can use the first Entitymanagerfactory created in main method in the entity EmployeeEntitySix while pre-persist, or else whether i can reuse the entitymanager created in first time in main method in the @PrePersist method in entity.

Just for information, I am using plain java environment, I am not using a Java EE container.

Upvotes: 0

Views: 981

Answers (1)

Tobias Lott
Tobias Lott

Reputation: 302

Hibernate by default tries to persist all fields of an entity class or embedded id, including the field emf, but it does not know how to persist a field of the type EntityManagerFactory.

Of course it does not make sense to persist an EntityManagerFactory. You could mark the field as @Transient to prevent it from being persisted, but then you are just going to face different problems.

The injection of an EntityManagerFactory with a @PersistenceUnit annotation only works on CDI Beans and EJBs in applications that run on a Java EE-compliant application server. As you are using a main method, I assume that your example is a simple JSE program.

Furthermore you should not access EntityManagers in lifecycle callback methods such as @PrePersist. A quote from the JPA Specification (JSR 317: JavaTM Persistence API, Version 2.0):

In general, the lifecycle method of a portable application should not invoke EntityManager or Query operations, access other entity instances, or modify relationships within the same persistence context. A lifecycle callback method may modify the non-relationship state of the entity on which it is invoked.

I suggest that you keep the EntityManagerFactory out of your embedded id class and also get rid of the incId-Method. Instead you could execute the query to determine the new employeeId in your main method, before calling persist. This works fine as long as only one instance of the program works with the database. When there are multiple programs trying to insert new employees there could be race conditions where the two programs try to insert the same id.

In order to prevent this you can use a database sequence to generate the employeeId, with the annotations @GeneratedValue and @SequenceGenerator. You find more information about id generation here: http://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing#Sequencing

Upvotes: 1

Related Questions