Вопрос по entity-relationship, jpa, jpa-2.0, java, jta – JPA с JTA: сохранить сущность и объединить каскадные дочерние сущности

18

У меня двунаправленное отношение «один ко многим» со следующими классами сущностей:

0 or 1 client <-> 0 or more product orders

При сохранении объекта клиента я хочу, чтобы связанные объекты заказа продукта также сохранялись (так как их внешний ключ к «родительскому» клиенту мог быть обновлен).

Конечно, все необходимые параметры CASCADE устанавливаются на стороне клиента. Ноit does not work if a newly created client is persisted for the first time while referencing an existing product order как в этом сценарии:

product order '1' is created and persisted. Works fine. client '2' is created and product order '1' is added to its product orders list. Then it is persisted. Does not work.

Я перепробовал несколько аппроксимаций, но ни один из них не показал ожидаемого результата. Смотрите эти результаты ниже. Я прочитал все связанные вопросы здесь, но они не помогли мне. Я использую EclipseLink 2.3.0, чистые аннотации JPA 2.0 и JTA в качестве типа транзакции в оперативной БД Apache Derby (JavaDB) на GlassFish 3.1.2. Отношения сущностей управляются графическим интерфейсом JSF. Управление отношениями на уровне объектов работает (не говоря уже о сохранении), я тестировал его с помощью тестов JUnit.

Approach 1) "Default" (based on NetBeans class templates)

Клиент:

@Entity
public class Client implements Serializable, ParentEntity {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @OneToMany(mappedBy = "client", cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH},
            fetch= FetchType.LAZY)
    private List<ProductOrder> orders = new ArrayList<>();

    // other fields, getters and setters
}

ProductOrder:

@Entity
public class ProductOrder implements Serializable, ChildEntity {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ManyToOne // owning side
    private Client client;

    // other fields, getters and setters
}

Общий Фасад Постоянства:

// Called when pressing "save" on the "create new..." JSF page
public void create(T entity) {
    getEntityManager().persist(entity);
}

// Called when pressing "save" on the "edit..." JSF page
public void edit(T entity) {
    getEntityManager().merge(entity);
}

Результат:

create () генерирует это исключение немедленно:

Warning: A system exception occurred during an invocation on EJB ClientFacade method public void javaee6test.beans.AbstractFacade.create(java.lang.Object) javax.ejb.EJBException: Transaction aborted ...

Caused by: javax.transaction.RollbackException: Transaction marked for rollback. ...

Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.0.v20110604-r9504): org.eclipse.persistence.exceptions.DatabaseException Internal Exception: java.sql.SQLIntegrityConstraintViolationException: The state-ment was aborted because it would have caused a duplicate key value in a unique or primary key constraint or unique index identified by 'SQL120513133540930' defined on 'PRODUCTORDER'. Error Code: -1 Call: INSERT INTO PRODUCTORDER (ID, CLIENT_ID) VALUES (?, ?) bind => [2 parameters bound] Query: InsertObjectQuery(javaee6test.model.ProductOrder[ id=1 ]) ...

Caused by: java.sql.SQLIntegrityConstraintViolationException: The statement was aborted because it would have caused a duplicate key value in a unique or primary key constraint or unique index identified by 'SQL120513133540930' defined on 'PRO-DUCTORDER'. ...

Caused by: org.apache.derby.client.am.SqlException: The statement was aborted be-cause it would have caused a duplicate key value in a unique or primary key con-straint or unique index identified by 'SQL120513133540930' defined on 'PRODUCTOR-DER'.

Я не понимаю этого исключения. edit () работает отлично. НО я хотел бы добавить заказы продукта клиенту во время его создания, так что этого недостаточно.

Approach 2) merge() only

Изменения в Generic Persistence Facade:

// Called when pressing "save" on the "create new..." JSF page
public void create(T entity) {
    getEntityManager().merge(entity);
}

// Called when pressing "save" on the "edit..." JSF page
public void edit(T entity) {
    getEntityManager().merge(entity);
}

Результат:

При создании () вывод журнала EclipseLink сообщает:

Fine: INSERT INTO CLIENT (ID, NAME, ADDRESS_ID) VALUES (?, ?, ?) bind => [3 parameters bound]

но НЕТ "ОБНОВЛЕНИЯ" на столе заказа продукции. Таким образом, отношения не установлены. Опять же, edit (), с другой стороны, работает нормально.

Apporach 3) Id GenerationType.IDENTITY on both entity types

Изменения в классе заказа клиента и продукта:

...
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...

Результат:

При создании () вывод журнала EclipseLink сообщает:

Fine: INSERT INTO CLIENT (NAME, ADDRESS_ID) VALUES (?, ?) bind => [2 parameters bound]

Fine: values IDENTITY_VAL_LOCAL()

Fine: INSERT INTO PRODUCTORDER (ORDERDATE, CLIENT_ID) VALUES (?, ?) bind => [2 parameters bound]

Fine: values IDENTITY_VAL_LOCAL()

таким образом, вместо установления отношения к заказу на продукт, добавленному в список клиента, создается и сохраняется новый объект заказа на продление (!) и устанавливается отношение к этому объекту. То же самое, edit () работает нормально.

Apporach 4) Подход (2) и (3) в сочетании

Результат: такой же, как подход (2).

Мой вопрос:Is there any way to realize the scenario described above? How can it be archieved? Я хотел бы остаться с JPA (нет решения для конкретного поставщика).

Ваш Ответ

4   ответа
7

Hi i had the same problem today, i ask to the openJPA mailing list with this email:

Привет. У меня проблема с вставкой и обновлением ссылки в той же сущности.

Я пытаюсь вставить новый объект (Exam), который имеет ссылку на другой объект (Person), и в то же время я хочу обновить атрибут (birthDate) объекта Person. Обновление никогда не происходит, хотя я установил CascadeType на ВСЕ. Единственный способ, которым это работает, - это сохранение и последующая операция слияния. Это нормально? Должен ли я что-то изменить ??

Мне не нравится идея "ручного обновления" использование слияния в объекте Person, потому что я не знаю, сколько объектов (дочерний объект экзамена) пользователь хочет обновить.

Объекты:

public class Exam{  
   @ManyToOne(cascade= CascadeType.ALL)
   @JoinColumn(name = "person_id")
   public Person person;
......
}

public class Person{
    private Date birthDate;
   @OneToMany(mappedBy = "person")
    private List<Exam> exams
.......
}

public class SomeClass{
   public void someMethod(){
      exam = new Exam()
      person.setBirthDate(new Date());
      exam.setPerson(person); 
      someEJB.saveExam(exam);
   }
}

public class someEJB(){

   public void saveExam(Exam exam){
        ejbContext.getUserTransaction().begin();
        em.persist(exam);
        //THIS WORKS
        em.merge(exam.getPerson());
        ejbContext.getUserTransaction().commit();       
   }

}

Должен ли я использовать метод MERGE для каждого дочернего объекта?

And the answer was this:

Похоже, ваша проблема в том, что экзамен новый, но человек существующий и существующий объект игнорируется при каскадном сохранении операция. Я считаю, что это работает, как ожидалось.

Пока ваши отношения установлены на CascadeType.ALL, вы всегда можете изменить свой em.persist (экзамен); сдать (экзамен); Это позаботится о сохраняя новый экзамен, и это также каскадный вызов слияния человек.

Спасибо, стог

Я надеюсь это тебе поможет.

0

см. Ниже.

   @Entity
   public class ProductOrder implements Serializable, ChildEntity {
   private static final long serialVersionUID = 1L;

   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private Long id;

   @ManyToOne // owning side
   @JoinColumn(name = "PRODUCTORDER_ID", referencedColumnName = "ID")
   //write your database column names into name and referencedColumnName attributes.
   private Client client;

   // other fields, getters and setters
   }
Нет, это не работает. Это на самом деле ничего не меняет. Я попробовал это с@JoinColumn(name = "CLIENT_ID", referencedColumnName = "ID") , но это не сработало. SputNick
1

@SputNick, я решил это с помощью шагов, описанных ниже. чтобы продемонстрировать, что я сделал,

У тебя естьClient entity [email protected] а такжеProductOrder entity [email protected] .Я использую

// Called when pressing "save" on the "edit..." 
public void edit(T entity) {
 getEntityManager().merge(entity);}

для настойчивости, поскольку это может дать мне гибкость, чтобы сохранить, а также обновить.

СоздаватьClient and corresponding Product orders использование

client.set..(some property to be saved);

productOrder.set..(some property to be saved);

// Установить productOrder для клиента

List<ProductOrder> order = new ArrayList<>();

client.setOrders(order); // Обратите внимание, что это ожидает, что список будет передан

// установить клиента для productOrder

productOrder.setClient(productOrder);

.edit(client) or .edit(productOrder)

Обратите внимание, что в любом случае вы сможете обновить обе таблицы сущностей с вашими изменениями.

Хоть и поздно, надеюсь, это кому-нибудь поможет

0

что вы устанавливаете обе стороны отношений, вы не можете просто добавить заказ клиенту, вам также нужно установить клиент заказа. Также вам нужно объединить оба объекта, которые вы изменили. Если вы просто объединяете клиента, то клиент заказа не будет объединен (хотя ваш каскад приводит к его объединению).

persist не работает, так как persist требует, чтобы сохраняемый объект был правильным для контекста постоянства, то есть не ссылался на отдельные объекты, он должен ссылаться на управляемые объекты.

Ваша проблема заключается в том, что вы отсоединяете объекты. Если бы вы не отсоединили объекты, у вас не возникло бы таких же проблем. Обычно в JPA вы создаете EntityManager, находите / запрашиваете ваши объекты, редактируете / сохраняете их, вызываете commit. Слияние не требуется.

@ DouglasDeR, izzoMeneghetti, потому что JPA все вместе с Hibernate отстой! 20 лет в отрасли, но пока что нет простого решения проблемы CASCADE MERGE. Это просто позор.
Спустя 4 года я должен был сделать то же самое. Сначала я следовал поэтапному подходу к использованию каскадных аннотаций только для JPA, но в итоге мне пришлось удалить аннотации и сохранить мои родительские сущности, добавить их обратно к каждой дочерней сущности и сохранить их с помощью слияния.
Я нашел обходной путь, похожий на ваши советы: я удалил всеcascade варианты, и я сохранил / слил & quot; в зависимости & quot; объекты, то есть путем ведения списка «в зависимости»; объекты и объединение их, когда «родительский» сохраняется / объединяется. НО я не приму этот ответ, так как он никогда не будет "официальным JPA" apporach. Также я не отсоединяю никакую сущность в явном виде, и в журнале EclipseLink не упоминается какая-либо операция отсоединения. Я также не понимаю, что этоcascade варианты для, поскольку они, очевидно, не работают. SputNick

Похожие вопросы