как openjpa2.0 улучшает сущности во время выполнения? - PullRequest
2 голосов
/ 09 июня 2010

Ниже мой тестовый код:

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 ]

1 голос
/ 10 июня 2010

Обратите внимание, что "Entity Manager отслеживает сущности с помощью ссылок на объекты Java."

Я могу только сохранять один и тот же объект / ссылку java снова и снова в цикле for, если итолько если я переместу оператор emf = Persistence.createEntityManagerFactory("basicPU") в цикл.как это:

Листинг 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.Почему так?

Я думаю, если у меня будет новый EntityManager в обоих листингах (1 и 2), я смогу сохранить один и тот же объект в базе данных, потому что EntityManager отслеживает сущности посредством ссылок на объекты Java и в каждомитерация У меня есть новый EntityManager, который не знает или не должен знать ссылки на объекты, сохраненные / сохраненные в базе данных на предыдущей итерации другим экземпляром объекта EntityManager.

1 голос
/ 09 июня 2010

Чтобы строго ответить на заголовок вопроса, улучшение во время выполнения выполняется с помощью javaagent, который можно динамически загружать при использовании JDK 1.6 (это описано в Enhancement Entity ).

Что касается проблемы, с которой вы столкнулись, я подозреваю, что это ошибка 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 новый экземпляр объекта имеет нет постоянной идентичности и еще не связан с постоянным контекстом.
  • управляемый экземпляр объекта - это экземпляр с постоянной идентификацией который в настоящее время связан с контекст постоянства.
  • A отсоединенный экземпляр объекта - это экземпляр с постоянной идентификацией то есть не (или больше) не связано с постоянным контекстом .
  • A удаленный экземпляр объекта - это экземпляр с постоянной идентификацией, связан с постоянством контекста, который планируется удалить из базы данных.

Таким образом, если у вас есть отдельная сущность (то есть , не связанная с постоянным контекстом ), и если вы установите для аннотированных полей Id значение null (таким образом, не будет постоянным) идентичность ), то у вас есть именно то, что спецификация определяет как новый объект, независимо от поведения OpenJPA. Вот почему я считаю, что это ошибка в OpenJPA (и в любом случае сообщение об ошибке является непоследовательным).

0 голосов
/ 09 июня 2010

Сообщение об ошибке сообщает вам, в чем проблема.

Попытка сохранить отсоединенный объект "jee.jpa2.Item-2".

После того, как вы сохраните свою сущность в первом цикле, все последующие вызовы em.persist (..) вы фактически передаете в отдельную сущность (не новую сущность). Установка поля id в NULL не означает, что объект теперь новый.

0 голосов
/ 09 июня 2010

OpenJPA 2.0 может динамически устанавливать средство улучшения времени выполнения при работе на JDK 1.6 (не JRE), см. Документы . Технически они динамически подключают Java-агент, используя Attach API , без необходимости -javaagent.

Итак, класс Item фактически улучшен, и менеджер сущностей знает, что он сохраняет отсоединенный экземпляр, несмотря на идентификатор null и закрытие менеджера сущностей.

0 голосов
/ 09 июня 2010

У вас был взгляд на исходный код?Это глубоко в недрах ядра:

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

Линия 2563

...