users@glassfish.java.net

Problem with JPA and cache

From: <glassfish_at_javadesktop.org>
Date: Tue, 15 Jul 2008 14:23:27 PDT

I'm having a problem with my J2EE application and after many hours of debugging
I have been able to distill the problem down into a small testable application.

My J2EE application consists of using the Command pattern and various Commands
can be strung together to accomplish a desired goal. These Commands when strung
together are executed within a Container Managed transaction and EntityManager.
This is the reason that the flow that I present below is done and I have no
easy way that I can change it.

Basically I have two entity classes: Parent and Child. Parent is created
with one Child automatically and other Child entities can be added. There
is a @OneToMany and @ManyToOne relationship between Parent and Child.

The problem that I am seeing is that I create a Parent, get another
handle to the parent using EntityManager.find, update the Parent,
create 3 more children for a total of 4, and commit. This commits fine
and the underlying database is correct. In a separate transaction, I find
the Parent and count the children which indicates that there are only
3 children instead of the 4 that are actually in the database.

From what I found, if I remove the @Version field on the parent, then
this problem goes away but then I cannot do optimistic locking.

Also in the code below, if I remove he "p1.setSerialNumber" invocation
the problem does not occur.

So I'm looking for some help into why this problem is occurring. I
think it has something to do with the @version and optimistic locking
but I don't know how to get around it.



Here is the main test application code. I build a small J2SE application
using user controlled transactions just for testing purposes and the
problem is still exhibited:

-- Main.java
package stest;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;

public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        Integer id = create();
        getCount(id);
    }

    public static Integer create() {
        Integer id = null;

        EntityManagerFactory emf = javax.persistence.Persistence.createEntityManagerFactory("stestPU");
        EntityManager em = emf.createEntityManager();
        em.getTransaction().begin();
        try {
            Parent p0 = new Parent(false);
            em.persist(p0);
            em.flush();

            Parent p1 = em.find(Parent.class, p0.getId());
            // VVVVVV
            p1.setSerialNumber("12345678");
            // ^^^^^^

            Child cs_1_1 = new Child();
            p1.addChild(cs_1_1);
            em.flush();

            Child cs_1_2 = new Child();
            p1.addChild(cs_1_2);
            em.flush();

            Parent chassis2 = em.find(Parent.class, p0.getId());
            Child cs_2_1 = new Child();
            chassis2.addChild(cs_2_1);
            em.flush();

            em.getTransaction().commit();
            id = p1.getId();
        } catch (Exception e) {
            e.printStackTrace();
            em.getTransaction().rollback();
        } finally {
            em.close();
        }
        return id;
    }

    public static void getCount(Integer id) {
        EntityManagerFactory emf = javax.persistence.Persistence.createEntityManagerFactory("stestPU");
        EntityManager em = emf.createEntityManager();
        em.getTransaction().begin();
        try {
            Parent chassis = em.find(Parent.class, id);
            int count = chassis.getChildren().size();
            System.out.println("config set count is " + count);
            em.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            em.getTransaction().rollback();
        } finally {
            em.close();
        }
    }
}
-- 
Here is the Parent Entity:
-- Parent.java
package stest;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Version;
@Entity
public class Parent implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column
    private Integer id;
    protected Parent() {
    }
    public Parent(boolean template) {
        Child child = new Child();
        addChild(child);
    }
    public Integer getId() {
        return this.id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    @Version
    @Column
    private int version;
    public int getVersion() {
        return version;
    }
    @OneToMany(mappedBy = "parent", cascade = {CascadeType.ALL})
    private List<Child> children = new ArrayList<Child>();
    public List<Child> getChildren() {
        return children;
    }
    public void setChildren(List<Child> children) {
        this.children = children;
    }
    public void addChild(Child cs) {
        cs.setParent(this);
        this.children.add(cs);
    }
    @Column
    private String serialNumber;
    public String getSerialNumber() {
        return this.serialNumber;
    }
    public void setSerialNumber(String serialNumber) {
        this.serialNumber = serialNumber;
    }
}
-- 
Here is the Child entity:
-- Child.java
package stest;
import java.io.Serializable;
import java.util.Date;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Version;
@Entity
public class Child implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column
    private Integer id;
    public Child() {
    }
    public Integer getId() {
        return this.id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    @Version
    @Column
    private int version;
    public int getVersion() {
        return version;
    }
    @ManyToOne(cascade = {CascadeType.ALL})
    private Parent parent;
    public Parent getParent() {
        return parent;
    }
    public void setParent(Parent parent) {
        this.parent = parent;
    }
    @Temporal(TemporalType.TIMESTAMP)
    @Column
    private Date createdOn = new Date();
    public Date getCreatedOn() {
        return this.createdOn;
    }
    protected void setCreatedOn(Date createdOn) {
        this.createdOn = createdOn;
    }
}
[Message sent by forum member 'bbergquist' (bbergquist)]
http://forums.java.net/jive/thread.jspa?messageID=286859