openjpa2.0如何在运行时增强实体?

时间:2010-06-09 11:22:22

标签: java orm jpa openjpa

以下是我的测试代码:

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)

请解释这里发生的事情?

5 个答案:

答案 0 :(得分:1)

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

对于您所遇到的问题,我怀疑这是一个错误的OpenJPA(我见过几个的类似的像OPENJPA-755问题),该错误信息是不连贯的与您的代码因为您将主键字段设置为null并且没有任何版本字段。你应该举报。

话虽这么说,解决问题的一种简单方法是在循环内创建一个新的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操作   实体实例的生命周期。一个   可以表征实体实例   作为新的,管理的,分离的,或   除去。

     
      
  • 实体实例没有持久身份,现在还没有   与持久化上下文相关联。
  •   
  • 托管实体实例是具有持久标识的实例   目前与a相关联   持久性上下文。
  •   
  • 分离的实体实例是具有持久标识的实例   那是没有(或不再)关联   持久化背景
  •   
  • 已删除实体实例是具有持久标识的实例,   与持久化上下文相关联,   计划从中删除   数据库中。
  •   

因此,如果你有一个分离的实体(即由此的不与持久性上下文相关联的),并如果设置了Id注释字段来null(它具有这样没有持久性身份),无论OpenJPA行为如何,您都具有规范定义为NEW实体的确切内容。这就是为什么我认为这是OpenJPA中的一个错误(错误信息无论如何都是不连贯的)。

答案 1 :(得分:1)

请注意,“实体管理器通过Java对象引用跟踪实体。”

当且仅当我在for循环中移动emf = Persistence.createEntityManagerFactory("basicPU")语句时,我才能在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循环的前一次迭代中持久存在的item对象。 一个大号 因为当我试图执行下面的行..

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对象实例在上一次迭代中保存/保存在数据库中的对象引用。

答案 2 :(得分:0)

答案 3 :(得分:0)

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

因此,Item类实际上已得到增强,并且实体管理器知道它仍然存在一个分离的实例,尽管null id和关闭实体管理器。

答案 4 :(得分:0)

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

  

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

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