2017-03-13 9 views
0

EclipseLink 2.5를 사용하는 응용 프로그램과 분리 된 엔티티를 수시로 병합해야하는 컨테이너 관리 트랜잭션에서 각 엔티티는 @Version 주석이있는 필드를 포함합니다. 엔티티가 DTO에 매핑되어 클라이언트로 보내지기 때문에 클라이언트는 해당 DTO에 대한 변경 사항을 기반으로 해당 엔티티에 대한 업데이트를 요청할 수 있으므로 낙관적 잠금을 구현해야합니다. 문제는 persist() 또는 merge()이 엔티티에서 호출 될 때 persist()의 경우 지속성 컨텍스트에 추가되는 해당 엔티티 또는 merge()이 반환하는 업데이트 된 엔티티에 업데이트 된 버전 필드가 포함되지 않는다는 것입니다.@Version의 JPA/EclipseLink 처리

@Stateless 
public class FooEjb { 

    @PersistenceContext(unitName = "FooApp") 
    private EntityManager entityManager; 


    public FooEntity create() { 

     FooEntity entity = new FooEntity(); 
     entity.setDescription("bar"); 

     entityManager.persist(entity); 

     return entity; 
    } 

    public FooEntity update(Long fooId, String description) { 
     FooEntity entityToUpdate = entityManager.find(FooEntity.class, fooId); 

     if (entityToUpdate != null) { 
      entityToUpdate.setDescription(description); 
      return entityManager.merge(entityToUpdate); 
     } 
     return null; 
    } 
} 

이러한 EJB 메소드 호출이 개체는 다음 지속됩니다

/다음과 같은 패션에 EJB에 합병

@Entity 
public class FooEntity implements Serializable { 

    @Id 
    @GeneratedValue(generator = "MyGenerator") 
    private Long fooId; 

    @Version 
    private Long version; 

    @Column(nullable = false) 
    private String description; 

    // generic setters and getters 
} 
: 예를 통해이를 설명하기 위해, 우리는 다음과 같은 기업이 있다고 가정

FooEntity newEntity = fooEjbInstance.create(); 
newEntity.getFooId(); // returns the generated, non-null value; for the sake of example 43L 
newEntity.getVersion(); // returns null 
entityManager.find(FooEntity.class, 43L).getVersion(); // returns 1L 

// entity with fooId == 42L exists and has a version value of 1L 
FooEntity updatedEntity = fooEjbInstance.update(42L, "fhtagn"); 
updatedEntity.getVersion(); // returns the old value, i.e. 1L 
entityManager.find(FooEntity.class, 42L).getVersion(); // returns 2L 

이 클라이언트에 의해 만들어진 모든 상태 변화를 지속 할 수 없으므로, 클라이언트에 전달하는 반환 된 엔티티가 적합하게 뒤 다음 동작을 보여줍니다 e를 병합/지속 호출로 변경하면 OptimisticLockException이 발생합니다.

EJB 메소드는 명시 적으로 @TransactionAttribute으로 주석 처리되지 않으며 JPA 스펙에 따라 TransactionAttributeType.REQUIRED의 기본값이 적용되어야합니다. 현재의 이론은 트랜잭션이 커밋 될 때만 업데이트되는 버전 필드와 관련이있는 현상입니다. 위의 EJB 메소드 중 하나가 반환 될 때까지는 관련 트랜잭션이 아직 커밋되지 않았기 때문에 (이 반환 된 후 즉시 이 커밋 됨) 버전 필드가 아직 업데이트되지 않았습니다. this question에 제출 된 버전의 객체 저장 및 캐시 저장에 대한 언급이 있었지만 이에 대한 명확한 문서를 찾을 수 없었습니다. JPA 2.0 또는 EclipseLink 구현에 따라 설계된대로 전체적으로 작동합니까? 그렇다면 앞에서 말한 문제를 어떻게 해결할 수 있을까요?

+4

병합은 버전 필드를 실제로 증가시키지 않습니다. 이는 명령문이 데이터베이스와 동기화 될 때만 발생하므로 발생하지 않습니다. 개체가 호출자에게 다시 직렬화 될 때 버전 필드를 변경하십시오. 메서드에서 em.flush() 메서드를 호출 해 봅니다.이 메서드는 컨텍스트를 동기화하고 개체가 반환되기 전에 필드를 증가시킵니다. – Chris

+0

@Chris 의미가 있습니다.이전 버전에 대한 검사가 수행 되었기 때문에 병합 중에 버전 필드가 증가한다는 오해의 여지가있었습니다. 그러나 다른 트랜잭션이 앞에서 설명한 병합 후 동일한 엔티티를 읽고 플러시하기 전에 기존 버전 값을 받고 엔티티 관리자가 플러시 할 경우 OLE가 병합 될 것이므로 실제 동작은 이상하게 보입니다. 그동안. 내 가정이 맞습니까? 그렇다면이 설계 결정에 대한 근거가 있습니까? –

+1

EntityManager 컨텍스트는 트랜잭션을 나타내므로 다른 컨텍스트/트랜잭션과 완전히 분리되어 있습니다. 트랜잭션이 커밋 될 때까지는 버전 및 기타 변경 사항을 다른 프로세스에서 가져올 수 없습니다. JPA는 대부분의 예외가 지속/병합 호출에서 발생하거나 작업의 성격에 따라 컨텍스트가 데이터베이스와 동기화 (플러시/커밋) 될 때까지 지연 될 수 있다고 설명합니다. 낙관적 인 잠금은 충돌이 자주 발생하지 않고 재 시도가 모든 작업을 비관적으로 잠그는 것보다 비용이 적게 드는 시스템을 의미합니다. – Chris

답변

1

Merge and persist는 즉시 데이터베이스에 갈 필요가 없으므로, 트리거, 시퀀싱 및 버전에 의존하는 작업은 플러시 또는 커밋을 호출하여 해당 값을 설정해야 할 수 있습니다. Flush를 사용하면 컨텍스트가 데이터베이스와 동기화되므로 관리 대상에 적절하게 값을 설정해야합니다. ID 생성은 지속 호출에서 설정할 수 있습니다. 시퀀스가 ​​사전 할당을 허용하지만 ID 객체 나 트리거가 사용되는 경우에는 일반적으로 허용되지 않습니다.

EntityManager 컨텍스트는 트랜잭션을 나타내므로 다른 컨텍스트/트랜잭션과 완전히 분리되어 있습니다. 트랜잭션이 커밋 될 때까지 다른 프로세스에서 버전 및 기타 변경 사항을 가져올 수 없기 때문에 동기화가 다른 프로세스에 발생하는시기는 중요하지 않습니다. JPA는 대부분의 예외가 지속/병합 호출에서 발생하거나 작업의 성격에 따라 컨텍스트가 데이터베이스와 동기화 (플러시/커밋) 될 때까지 지연 될 수 있다고 설명합니다. 낙관적 인 잠금은 충돌이 자주 발생하지 않고 재 시도가 모든 작업에서 비관적 인 잠금보다 저렴함을 의미합니다.