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

「EntityManagerはJavaオブジェクト参照を通じてエンティティを追跡します」ということに注意してください。

emf = Persistence.createEntityManagerFactory("basicPU")ステートメントをforループに移動した場合にのみ、同じJavaオブジェクト/参照をforループで何度も永続化でき ます。このような:

リスト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()メソッドが、forループの前の反復で永続化されたアイテムオブジェクトを追跡しているEntityManagerのキャッシュされたコピーを返すことを意味しますか? 大きないいえ。 以下の行を実行しようとしたとき。

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 に答える
1

質問のタイトルに厳密に答えるために、ランタイム拡張は、JDK 1.6を使用するときに動的にロードできるjavaagentを使用して行われます(これはエンティティ拡張に記載されています)。

あなたが直面している問題に関しては、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操作について説明します。エンティティインスタンスは、新規、管理、分離、または削除されたものとして特徴付けることができます。

  • 新しいエンティティインスタンスには永続的IDがなく、永続コンテキストにまだ関連付けられていません。
  • 管理対象エンティティインスタンスは、現在永続コンテキストに関連付けられている永続IDを持つインスタンスです。
  • デタッチされたエンティティインスタンスは、永続コンテキストに関連付けられていない(または関連付けられていない)永続IDを持つインスタンスです。
  • 削除されたエンティティインスタンスは、データベースからの削除がスケジュールされている、永続コンテキストに関連付けられた永続IDを持つインスタンスです。

したがって、デタッチされたエンティティ(したがって永続コンテキストに関連付けられていないId)があり、注釈付きフィールドをnull(したがって永続IDがない)に設定した場合、仕様に関係なく、新しいエンティティとして定義されているものが正確に得られます。 OpenJPAの動作。これがOpenJPAのバグであると私が考える理由です(そしてエラーメッセージはとにかく一貫性がありません)。

于 2010-06-09T15:19:54.517 に答える
0

エラーメッセージは、問題の内容を示しています。

切り離されたオブジェクト「jee.jpa2.Item-2」の永続化を試みます。

最初のループでエンティティを永続化した後、その後の em.persist(..) へのすべての呼び出しで、実際には切り離されたエンティティ (新しいエンティティではない) を渡します。id フィールドを NULL に設定しても、エンティティが新しくなったわけではありません。

于 2010-06-09T17:08:44.597 に答える
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

したがって、クラスは実際に拡張され、エンティティ マネージャーは、 id とエンティティ マネージャーの終了にItemもかかわらず、分離されたインスタンスを保持していることを認識します。null

于 2010-06-09T13:18:35.737 に答える