문제

Below is my test code:

package jee.jpa2;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.Query;

import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@Test
public class Tester {
    EntityManager em;
    EntityTransaction tx;
    EntityManagerFactory emf;

    @BeforeClass
    public void setup() {
        emf = Persistence.createEntityManagerFactory("basicPU", System.getProperties());
    }

    @Test
    public void insert() {
        Item item = new Item();
        for (int i = 0; i < 1000; ++i) {
            em = emf.createEntityManager();
            tx = em.getTransaction();
            tx.begin();
            item.setId(null);
            em.persist(item);
            tx.commit();
            em.clear();
            em.close();
            tx=null;
            em=null;
        }
    }

    @Test
    public void read() {
        em = emf.createEntityManager();
        tx = em.getTransaction();
        tx.begin();
        Query findAll = em.createNamedQuery("findAll");
        List<Item> all = findAll.getResultList();
        for (Item item : all) {
            System.out.println(item);
        }
        tx.commit();
    }
}

And here is the entity:

package jee.jpa2;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQuery;

@Entity
@NamedQuery(name="findAll", query="SELECT i FROM Item i")
public class Item {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID", nullable = false, updatable= false)
    protected Long id;
    protected String name;

    public Item() {
        name = "Digambar";
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return String.format("Item [id=%s, name=%s]", id, name);
    }

}

After executing test I get Error:

Item [id=1, name=Digambar]
Item [id=2, name=Digambar]
PASSED: read
FAILED: insert
<openjpa-2.0.0-r422266:935683 nonfatal store error> org.apache.openjpa.persistence.EntityExistsException: Attempt to persist detached object "jee.jpa2.Item-2".  If this is a new instance, make sure any version and/or auto-generated primary key fields are null/default when persisting.
FailedObject: jee.jpa2.Item-2
    at org.apache.openjpa.kernel.BrokerImpl.persist(BrokerImpl.java:2563)
    at org.apache.openjpa.kernel.BrokerImpl.persist(BrokerImpl.java:2423)
    at org.apache.openjpa.kernel.DelegatingBroker.persist(DelegatingBroker.java:1069)
    at org.apache.openjpa.persistence.EntityManagerImpl.persist(EntityManagerImpl.java:705)
    at jee.jpa2.Tester.insert(Tester.java:33)

Please Explain whats happening here?

도움이 되었습니까?

해결책

To strictly answer the question title, runtime enhancement is done using a javaagent that can be dynamically loaded when using JDK 1.6 (this is documented in Entity Enhancement).

Regarding the problem you're facing, I suspect it to be an OpenJPA bug (I've seen several similar issues like OPENJPA-755), the error message is incoherent with your code since you're setting the primary key field to null and don't have any version field. You should report it.

That being said, an easy way to "workaround" the issue would be to create a new Item instance inside the loop. Something like this:

public void insert() {
    for (int i = 0; i < 1000; ++i) {
        Item item = new Item();
        em = emf.createEntityManager();
        tx = em.getTransaction();
        tx.begin();
        em.persist(item);
        tx.commit();
        em.clear();
        em.close();
        tx=null;
        em=null;
    }
}

Additional notes:

Why are you passing System.getProperties() when creating the EntityManagerFactory?

You should close the EntityManagerFactory at the end of your test:

@AfterClass
public static void closeEntityManagerFactory() {
    emf.close();
}

Update: To answer a comment. From the JPA specification:

3.2 Entity Instance’s Life Cycle

This section describes the EntityManager operations for managing an entity instance’s lifecycle. An entity instance may be characterized as being new, managed, detached, or removed.

  • A new entity instance has no persistent identity, and is not yet associated with a persistence context.
  • A managed entity instance is an instance with a persistent identity that is currently associated with a persistence context.
  • A detached entity instance is an instance with a persistent identity that is not (or no longer) associated with a persistence context.
  • A removed entity instance is an instance with a persistent identity, associated with a persistence context, that is scheduled for removal from the database.

So if you have a detached entity (that is thus not associated with a persistent context) and if you set the Id annotated fields to null (it has thus no persistent identity), then you have exactly what the specification defines as a NEW entity, regardless of OpenJPA behavior. That's why I consider this to be a bug in OpenJPA (and the error message is incoherent anyway).

다른 팁

Point to note that is "Entity Manager keeps track of entities through Java Object References."

I can only persist same java object/reference again and again in for loop if and only if I move emf = Persistence.createEntityManagerFactory("basicPU") statement in for loop. like this:

Listing 1:

Item item = new Item(); 
    for (int i = 0; i < 1000; ++i) { 
        emf = Persistence.createEntityManagerFactory("basicPU");            
        em = emf.createEntityManager();         
        tx = em.getTransaction();
        tx.begin(); 
        item.setId(null); 
        em.persist(item); 
        tx.commit();
        em.clear();
        em.close(); 
    } 

But if the for loop is like :

Listing 2:

Item item = new Item(); 
    emf = Persistence.createEntityManagerFactory("basicPU");     
    for (int i = 0; i < 1000; ++i) { 
        em = emf.createEntityManager();         
        tx = em.getTransaction();
        tx.begin(); 
        item.setId(null); 
        em.persist(item); 
        tx.commit();
        em.clear();
        em.close(); 
    } 

I get the EntityExistException.

Does it mean that emf.createEntityManager() method returns the cached copy of EntityManager who is keeping track of item object persisted in previous iteration of for loop.? a BIG NO. because when I tried to execute below lines..

emf = Persistence.createEntityManagerFactory("basicPU");
        for (int i = 0 ; i<10; i++){
                System.out.println(emf.createEntityManager());
        }

It printed..

org.apache.openjpa.persistence.EntityManagerImpl@18105e8
org.apache.openjpa.persistence.EntityManagerImpl@9bad5a
org.apache.openjpa.persistence.EntityManagerImpl@91f005
org.apache.openjpa.persistence.EntityManagerImpl@1250ff2
org.apache.openjpa.persistence.EntityManagerImpl@3a0ab1
org.apache.openjpa.persistence.EntityManagerImpl@940f82
org.apache.openjpa.persistence.EntityManagerImpl@864e43
org.apache.openjpa.persistence.EntityManagerImpl@17c2891
org.apache.openjpa.persistence.EntityManagerImpl@4b82d2
org.apache.openjpa.persistence.EntityManagerImpl@179d854

Which means emf.createEntityManager() always returns new instance of entityManager.

So in Listing 1 and Listing 2, I always gets new EntityManager instance and still with Listing 1 I can persist same object in database but in Listing 2 I get EntityExistException. Why So?

I think If I have new EntityManager in both Listings(1 and 2) I should be able to persist same object in database because EntityManager keeps track of entities through java object references and In each iteration I have new EntityManager, which dont know or should not know the object references saved/persisted in database in previous iteration by some other EntityManager object instance.

Have you had a peek at the source code? It's deep in the bowels of the kernel:

http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/BrokerImpl.java?view=markup

Line 2563

OpenJPA 2.0 can dynamically install runtime enhancer when running on JDK 1.6 (not JRE), see docs. Technically, they attach a java agent dynamically using Attach API, wihtout need for -javaagent option.

So, the Item class is actually enhanced and entity manager knows that it's persisting a detached instance despite the null id and closing of entity manager.

The error message tells you what the problem is.

Attempt to persist detached object "jee.jpa2.Item-2".

After you persist your Entity on the first loop, all subsequent calls to em.persist(..) you are actually passing in a detached Entity (Not a new Entity). Setting the id field to NULL does not mean that the Entity is now new.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top