2

下面是我的测试代码:

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();
    }
}

这是实体:

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);
    }

}

执行测试后出现错误:

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)

请解释这里发生了什么?

4

5 回答 5

1

为了严格回答问题标题,运行时增强是使用 javaagent 完成的,该 javaagent 在使用 JDK 1.6 时可以动态加载(这在Entity Enhancement中有记录)。

关于您面临的问题,我怀疑它是一个 OpenJPA 错误(我已经看到了几个类似的问题,例如OPENJPA-755),错误消息与您的代码不一致,因为您将主键字段设置为null和 don '没有任何版本字段。你应该报告它。

话虽如此,“解决”该问题的一种简单方法是Item在循环内创建一个新实例。像这样的东西:

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;
    }
}

补充笔记:

为什么你System.getProperties()在创建时通过EntityManagerFactory

您应该EntityManagerFactory在测试结束时关闭:

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

更新:回答评论。从 JPA 规范:

3.2 实体实例的生命周期

本节介绍用于管理实体实例生命周期的 EntityManager 操作。实体实例的特征可以是新的、托管的、分离的或已删除的。

  • 一个的实体实例没有持久性标识,并且还没有与持久性上下文相关联。
  • 托管实体实例是具有当前与持久性上下文相关联的持久身份的实例。
  • 分离的实体实例是具有不(或不再)与持久性上下文相关联的持久身份的实例。
  • 已删除的实体实例是具有持久身份的实例,与持久性上下文相关联,计划从数据库中删除。

因此,如果您有一个分离的实体(因此不与持久性上下文相关联)并且如果您将带Id注释的字段设置为null(因此它没有持久性身份),那么您将拥有规范定义为新实体的内容,而不管OpenJPA 行为。这就是为什么我认为这是 OpenJPA 中的一个错误(并且错误消息无论如何都是不连贯的)。

于 2010-06-09T15:19:54.517 回答
1

需要注意的是“实体管理器通过 Java 对象引用跟踪实体”。

emf = Persistence.createEntityManagerFactory("basicPU")当且仅当我在 for 循环中移动语句时,我只能在 for 循环中一次又一次地保留相同的 java 对象/引用 。像这样:

清单 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(); 
    } 

但是如果 for 循环是这样的:

清单 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(); 
    } 

我得到了 EntityExistException。

这是否意味着 emf.createEntityManager() 方法返回 EntityManager 的缓存副本,该副本正在跟踪在 for 循环的先前迭代中持久存在的项目对象。? 一个大号。 因为当我尝试执行以下行时..

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

打印出来了。。

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

这意味着 emf.createEntityManager() 总是返回 entityManager 的新实例。

所以在清单 1 和清单 2 中,我总是获得新的 EntityManager 实例,并且仍然使用清单 1,我可以将相同的对象保存在数据库中,但在清单 2 中,我得到了 EntityExistException。为什么这样?

我认为如果我在清单(1 和 2)中都有新的 EntityManager,我应该能够在数据库中保留相同的对象,因为 EntityManager 通过 java 对象引用跟踪实体,并且在每次迭代中我都有新的 EntityManager,它不知道或不应该知道其他一些 EntityManager 对象实例在以前的迭代中保存/保留在数据库中的对象引用。

于 2010-06-10T06:01:48.873 回答
0

你看过源代码吗?它在内核的深处:

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

2563行

于 2010-06-09T12:55:10.697 回答
0

OpenJPA 2.0 在 JDK 1.6(不是 JRE)上运行时可以动态安装运行时增强器,请参阅 docs从技术上讲,他们使用Attach API动态附加 java 代理,无需-javaagent选项。

因此,Item该类实际上得到了增强,并且实体管理器知道它正在持久化一个分离的实例,尽管null实体管理器的 id 和关闭。

于 2010-06-09T13:18:35.737 回答
0

错误消息告诉您问题所在。

尝试保留分离的对象“jee.jpa2.Item-2”。

在第一个循环中持久化实体后,对 em.persist(..) 的所有后续调用实际上是在传递一个分离的实体(不是新实体)。将 id 字段设置为 NULL 并不意味着该实体现在是新的。

于 2010-06-09T17:08:44.597 回答