JPA z JTA: Persist i połącz kaskadowe encje potomne

Mam dwukierunkową relację jeden do wielu z następującymi klasami encji:

0 lub 1 klient <-> 0 lub więcej zamówień na produkty

Podczas utrwalania jednostki klienta chcę, aby powiązane jednostki zamawiania produktów były również utrwalone (ponieważ ich klucz obcy dla klienta „macierzystego” mógł zostać zaktualizowany).

Oczywiście wszystkie wymagane opcje CASCADE są ustawiane po stronie klienta. Alenie działa, jeśli nowo utworzony klient jest utrwalany po raz pierwszy podczas odwoływania się do istniejącego zamówienia produktu jak w tym scenariuszu:

zamówienie produktu „1” zostało utworzone i utrwalone. Działa w porządku.klient „2” jest tworzony, a zamówienie „1” jest dodawane do listy zamówień produktów. Potem trwa. Nie działa.

Próbowałem kilku apporaches, ale żaden nie pokazywał oczekiwanego rezultatu. Zobacz te wyniki poniżej. Przeczytałem tutaj wszystkie powiązane pytania, ale mi nie pomogli. Używam EclipseLink 2.3.0, czystych adnotacji JPA 2.0 i JTA jako typu transakcji na bazie danych pamięci Apache Derby (JavaDB) na GlassFish 3.1.2. Relacje encji są zarządzane przez GUI JSF. Zarządzanie relacjami na poziomie obiektów działa (oprócz trwałości), przetestowałem je za pomocą testów JUnit.

Podejście 1) „Domyślne” (na podstawie szablonów klas NetBeans)

Klient:

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

Zamówienie produktu:

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

Generic Persistence Facade:

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

Wynik:

create () natychmiast rzuca ten wyjątek:

Ostrzeżenie: Wystąpił wyjątek systemowy podczas wywołania metody EJB ClientFacade public void javaee6test.beans.AbstractFacade.create (java.lang.Object) javax.ejb.EJBException: Transakcja przerwana ...

Przyczyna: javax.transaction.RollbackException: transakcja oznaczona do wycofania. ...

Przyczyna: wyjątek [EclipseLink-4002] (usługi utrwalania Eclipse - 2.3.0.v20110604-r9504): wyjątek wewnętrzny org.eclipse.persistence.exceptions.DatabaseException: wyjątek java.sql.SQLIntegrityConstraintViolationException: stan został przerwany, ponieważ spowodowały zduplikowaną wartość klucza w unikalnym lub podstawowym kluczu lub unikalnym indeksie identyfikowanym przez „SQL120513133540930” zdefiniowanym w „PRODUCTORDER”. Kod błędu: -1 Zadzwoń: INSERT IN PRODUCTORDER (ID, CLIENT_ID) VALUES (?,?) Bind => [2 powiązane parametry] Query: InsertObjectQuery (javaee6test.model.ProductOrder [id = 1]) ...

Przyczyna: java.sql.SQLIntegrityConstraintViolationException: instrukcja została przerwana, ponieważ spowodowałoby to zduplikowanie wartości klucza w unikalnym lub podstawowym kluczu lub unikalnym indeksie identyfikowanym przez „SQL120513133540930” zdefiniowanym w „PRO-DUCTORDER”. ...

Przyczyna: org.apache.derby.client.am.SqlException: instrukcja została przerwana, ponieważ spowodowałoby to zduplikowanie wartości klucza w unikalnym lub podstawowym kluczu ograniczającym lub unikalnym indeksie identyfikowanym przez „SQL120513133540930” zdefiniowane w „ PRODUCENT-DER ”.

Nie rozumiem tego wyjątku. edit () działa dobrze. ALE chciałbym dodać zamówienia produktu do klienta w czasie jego tworzenia, więc jest to niewystarczające.

Podejście 2) tylko scal ()

Zmiany w fasadowej trwałości:

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

Wynik:

W przypadku create () wyjście EclipseLink Logging mówi:

Fine: INSERT IN CLIENT (ID, NAME, ADDRESS_ID) WARTOŚCI (?,?,?) Bind => [3 powiązane parametry]

ale BRAK „AKTUALIZACJI” w tabeli zamówień produktów. Zatem związek nie jest ustalony. Z drugiej strony, edit () działa dobrze.

Apporach 3) Id GenerationType.IDENTITY na obu typach jednostek

Zmiany zarówno klasy klienta, jak i klasy produktu:

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

Wynik:

W przypadku create () wyjście EclipseLink Logging mówi:

Fine: INSERT IN CLIENT (NAZWA, ADDRESS_ID) WARTOŚCI (?,?) Bind => [2 powiązane parametry]

Dobra: wartości IDENTITY_VAL_LOCAL ()

Fine: INSERT IN PRODUCTORDER (ORDERDATE, CLIENT_ID) VALUES (?,?) Bind => [2 powiązane parametry]

Dobra: wartości IDENTITY_VAL_LOCAL ()

w związku z tym zamiast ustalania relacji do zamówienia produktu dodanego do listy klientów, tworzony jest i utrzymywany jest nowy podmiot zamówienia produkcyjnego (!), a relacja z tym podmiotem zostaje ustalona. To samo, edit () działa dobrze.

Apporach 4) Podejście (2) i (3) połączone

Wynik: Tak samo jak podejście (2).

Moje pytanie brzmi:Czy jest jakiś sposób na realizację scenariusza opisanego powyżej? Jak można go zdobyć? Chciałbym pozostać przy JPA (brak rozwiązania specyficznego dla dostawcy).

questionAnswers(4)

yourAnswerToTheQuestion