2016-12-14 8 views
0

커다란 JPA 주석이있는 엔티티를 통해 생성 및 병합해야하는 WebSphere 8.5.5 (OpenJPA 2.2.3)의 프로젝트에서 작업하고 있습니다. 모델. Grand-parent에서 EntityManager.merge()를 호출하거나 트랜잭션 커밋에서 트리거 된 플러시를 통해 grand-children을 병합 할 때 매우 특정한 문제가 발생합니다. 여기서, 세부 사항은 :복합 EmbeddedId (다른 EmbeddedId를 포함하는 EmbeddedId)가 포함 된 JPA 엔티티의 순서 삽입

엔티티 매핑

주요부 :

  1. EntityA는
  2. EntityB는
  3. EntityC는
을 EntityD하는 oneToMany을 갖는다 EntityC에 oneToMany을 갖는다 EntityB하는 oneToMany을 갖는다

모두 양방향 매핑이 있습니다. 엔티티 A와 B에는 단일 열 기본 키가 있습니다. 엔티티 C에는 엔티티 B의 기본 키에 대한 외래 키가 포함 된 복합 기본 키가 있습니다. 엔티티 D에는 엔티티 C의 복합 키가 포함 된 복합 키가 있습니다. 아래 매핑을 참조하십시오.

당신은 올바르게 유지 계단식으로하고 모델 (연결된 모든 어린이들과) 엔티티 A에 EntityManager.persist()를 호출 할 수 있습니다 : 어떤 작품

@Entity 
@Table(name="TableA") 
public class EntityA extends BaseEntity { 

    @Id 
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="TABLE_A_ID_GEN") 
    @SequenceGenerator(name="TABLE_A_ID_GEN", sequenceName="TABLE_A_ID", allocationSize=1) 
    @Column(name="TABLE_A_ID") 
    private Integer id; 

    @OneToMany(fetch=FetchType.LAZY, mappedBy="entityA", cascade=CascadeType.ALL) 
    private List<EntityB> entityBList; 

    ... 

} 

@Entity 
@Table(name="TableB") 
public class EntityB extends BaseEntity { 

    @Id 
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="TABLE_B_ID_GEN") 
    @SequenceGenerator(name="TABLE_B_ID_GEN", sequenceName="TABLE_B_ID", allocationSize=1) 
    @Column(name="TABLE_B_ID") 
    private Integer id; 

    @ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.ALL) 
    @JoinColumn(name="TABLE_A_ID") 
    private EntityA entityA; 

    @OneToMany(fetch=FetchType.LAZY, mappedBy="entityB", cascade=CascadeType.ALL) 
    private List<EntityC> entityCList; 

    ... 

} 

@Entity 
@Table(name="TableC") 
public class EntityC extends BaseEntity { 

    @EmbeddedId 
    private EntityC_PK id = new EntityC_PK(); 

    @MapsId("entityB_Id") 
    @ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.ALL) 
    @JoinColumn(name="TABLE_B_ID") 
    private EntityB entityB; 

    @OneToMany(fetch=FetchType.LAZY, mappedBy="entityC", cascade=CascadeType.ALL) 
    private List<EntityD> entityDList; 

    ... 

} 

@Embeddable 
public class EntityC_PK implements BaseComponent { 

    @Column(name="TABLE_B_ID", nullable = false, updatable = false) 
    private Integer entityB_Id; 

    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="TABLE_C_ID_GEN") 
    @SequenceGenerator(name="TABLE_C_ID_GEN", sequenceName="TABLE_C_ID", allocationSize=1) 
    @Column(name="TABLE_C_ID") 
    private Integer entityC_Id; 

    ... 

} 

@Entity 
@Table(name="TABLE_D") 
public class EntityD extends BaseEntity { 

    @EmbeddedId 
    private EntityD_PK id = new EntityD_PK(); 

    @MapsId("entityC_Id") 
    @JoinColumns({ 
     @JoinColumn(name = "TABLE_B_ID"), 
     @JoinColumn(name = "TABLE_C_ID")}) 
    @ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.ALL) 
    private EntityC entityC; 

    ... 

} 

@Embeddable 
public class EntityD_PK implements BaseComponent { 

    @Embedded 
    private EntityC_PK entityC_Id; 

    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="TABLE_D_ID_GEN") 
    @SequenceGenerator(name="TABLE_D_ID_GEN", sequenceName="TABLE_D_ID", allocationSize=1) 
    @Column(name="TABLE_D_ID") 
    private Integer entity_id; 

    ... 

} 

. 당신이 EntityManager.persist (entityA를) 엔티티 A를 인스턴스화하고 호출하는 경우

다음 등 어린이, 그랜드 - 어린이를 추가 할 때 EntityManager.merge (entityA) (또는 허용 :

무엇을 작동하지 않는다 트랜잭션을 커밋 할 때 암시 적으로 병합) 올바른 순서로 INSERT 문을 실행하지 못합니다. 일을 더 혼란스럽게 만들기 위해 INSERT의 순서는 단위 테스트의 반복 실행간에 일관성이 없습니다.

우리가 어떻게 병합시 JPA 주석 올바른 삽입 순서를 시행 (및 업데이트/삭제) 정정 : 그것은 엔티티 C.

전에 질문을 엔티티 D를 삽입을 시도하여 실패?

EDIT 1 : 데이터베이스가 제약 조건과 외래 키 관계를 적용하기 때문에 삽입/삭제 순서가 중요합니다.

+0

사용 가능한 내 대답을 참조하십시오. http://stackoverflow.com/questions/39024310/openjpa-nested-onetomany-relationships-merge-issue/39025865#39025865 –

답변

2

시나리오에 대한 JPA 스펙을 검토해야한다는 것을 먼저 알리 자. (아마도 미안하다. 미안하다.) 임베디드에는 때로는 서로 다른 규칙이 적용된다. 다음으로, 당신은 'EntityManager.create()'라고 진술하지만, 당신이 .persist를 의미한다고 생각합니까? 나중에 병합에 관해서 이야기하니, 아마도 당신은 병합을 의미할까요? 어쨌든 병합보다는 새로운 엔티티를 유지하려는 경우 .persist를 사용하는 것이 좋습니다. 불법은 아니지만 일반적으로 분리 된 개체 등을 병합하는 것이 병합입니다.

그런 의미에서 질문의 핵심에 도달하고 주문에 도움이되는 속성을 제공하겠습니다. ddl에 외래 키 제약 조건이 포함되어 있으면 텍스트에 진술하지 않았습니다. 당신이 질서에 관심이 있기 때문에, 나는 당신에게 그런 제약이 있다고 가정 할 것입니다. 그렇게한다면, OpenJPA는이 제약 조건에 대해 아무 것도 모릅니다. 따라서, 물건을 적절히 주문하는 것을 알지 못할 것입니다. 기본적으로 SQL의 순서에 의존 할 수는 없으며 주문의 무작위성은 내가 예상 한 것입니다. 그러나 FK 제약 조건을 지원하는 것과 같은 방식으로 주문해야하는 경우 OpenJPA에서 제약 조건을 '배울'수 있도록해야합니다. 그렇게하려면이 속성을 지속성으로 설정해야합니다.XML 파일 (또는 당신은 JVM 사용자 정의 특성으로 설정할 수 있습니다) :

<property name="openjpa.jdbc.SchemaFactory" value="native(ForeignKeys=true)"/> 

이 속성는 OpenJPA는 스키마 그래서 그것은 당신의 FK 제약 조건에 대해 배울 수있는 일에 검사 할 수 있습니다. 그 지식으로, OpenJPA는 적절하게 SQL을 주문할 수 있습니다. 당신은 FK 제약 조건이없는,하지만 당신은 어떤 방법으로 SQL을 주문하려면

마지막으로, 당신이 사용해야 할 수도 있습니다 :

<property name="openjpa.jdbc.UpdateManager" value="operation-order"/> 

는하지 마십시오, 내가 할 반복 이 두 속성을 함께 사용하지 마십시오. 이상한 부작용이있을 수 있습니다. 먼저 SchemaFactory 속성에 초점을 맞추고 UpdateManager를 시도하는 데 도움이되지 않으면 초점을 맞추십시오. 조작 순서는 OpenJPA에게 엔티티를 유지하는 방법, 즉 조작 순서를 기반으로 SQL을 정렬하도록 지시합니다. 이것은 A를 지속하고 다른 모든 것이 계단식으로 연결되기를 기대하기 때문에 실제로 상황에 지나치게 도움이되지 않을 수 있습니다 (OpenJPA는 첫 번째로 지속되지만, B와 C에서는 첫 번째로가는 것이 좋습니다). 그러나 A, C, B를 유지했다면 SQL은 A, C, B를 차례로 넣은 다음 "operation-order"를 설정해야합니다.

+0

도움이 될만한 답변을 주셔서 감사합니다. 귀하의 솔루션과 정확히 일치하는 더 많은 정보를 발견했습니다. [link] (http://openjpa.208410.n2.nabble.com/Inconsistent-execution-order-of-INSERT-statements-using-cascading-persist-td678745.html). 언급하지 않은 한 가지 옵션은 OpenJPA의 @ForeignKey 주석을 사용하여 어떤 관계에 외래 키가 있고 특정 삽입 순서가 필요한지를 표시하는 것입니다. –

+1

감사합니다. 나는 그것을 의도적으로 버렸다. OpenJPA의 '@ForeignKey'에는 두 가지 문제가 있습니다. 1) 타당성이 있습니다. 2) 개발자에게 '@ForeignKey'를 모든 올바른 위치에 놓으려면 부담을 둡니다 (한 지점을 놓치고 모든 베팅이 꺼짐). 귀하의 경우에는 몇 가지 클래스 큰 거래,하지만 큰 애플 리케이션에서 일을 더 어렵게 만듭니다. 나는 '@FK'를 사용하지 않겠다고 말하지만 도메인 모델의 오른쪽 지점에 '@FK'를 추가하는 것보다 한 줄짜리 속성 (코드 변경 없음)을 사용하는 것을 진지하게 고려할 것입니다. 행운을 빌어 요! –