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 구현에 따라 설계된대로 전체적으로 작동합니까? 그렇다면 앞에서 말한 문제를 어떻게 해결할 수 있을까요?
병합은 버전 필드를 실제로 증가시키지 않습니다. 이는 명령문이 데이터베이스와 동기화 될 때만 발생하므로 발생하지 않습니다. 개체가 호출자에게 다시 직렬화 될 때 버전 필드를 변경하십시오. 메서드에서 em.flush() 메서드를 호출 해 봅니다.이 메서드는 컨텍스트를 동기화하고 개체가 반환되기 전에 필드를 증가시킵니다. – Chris
@Chris 의미가 있습니다.이전 버전에 대한 검사가 수행 되었기 때문에 병합 중에 버전 필드가 증가한다는 오해의 여지가있었습니다. 그러나 다른 트랜잭션이 앞에서 설명한 병합 후 동일한 엔티티를 읽고 플러시하기 전에 기존 버전 값을 받고 엔티티 관리자가 플러시 할 경우 OLE가 병합 될 것이므로 실제 동작은 이상하게 보입니다. 그동안. 내 가정이 맞습니까? 그렇다면이 설계 결정에 대한 근거가 있습니까? –
EntityManager 컨텍스트는 트랜잭션을 나타내므로 다른 컨텍스트/트랜잭션과 완전히 분리되어 있습니다. 트랜잭션이 커밋 될 때까지는 버전 및 기타 변경 사항을 다른 프로세스에서 가져올 수 없습니다. JPA는 대부분의 예외가 지속/병합 호출에서 발생하거나 작업의 성격에 따라 컨텍스트가 데이터베이스와 동기화 (플러시/커밋) 될 때까지 지연 될 수 있다고 설명합니다. 낙관적 인 잠금은 충돌이 자주 발생하지 않고 재 시도가 모든 작업을 비관적으로 잠그는 것보다 비용이 적게 드는 시스템을 의미합니다. – Chris