2009-08-24 3 views
1

오늘 트랜잭션이 어떻게 작동하는지 확인하기 위해 응용 프로그램의 테스트 케이스를 코딩했습니다. 그리고 나는 그것이 내가 생각했던대로 아무것도 작동하지 않는다는 것을 알았다.JPA 및 Spring 트랜잭션 - 설명해주십시오.

MySQL 기반의 JPA 프로 바이더로서 Hibernate를 사용하는 Spring 기반 애플리케이션이 있습니다. 스프링의 JpaDaoSupport를 확장하는 DAO 객체가 있습니다. 이것들은 Spring의 트랜잭션 관리에 의해 다루어진다.

내가 이런 작품을 만든 테스트 케이스 : 1) 엔티티가에 DAO 방법 incrementCounter()를 호출 모두 0 2로 설정 일부 카운터) 그리고 두 개의 스레드가 생성으로 만들어집니다에게 고리.

DAO 메소드가 트랜잭션으로 덮여있을 때 하나의 스레드 만 내부에 있다고 생각했습니다. 즉, 스프링이 동기화를 처리합니다. 그러나 이것은 잘못된 가정으로 입증되었습니다.

synchronized을 DAO 메소드에 추가하여 (일시적으로)이 문제를 해결 한 후, Hibernate는 DAO 메소드에 의해 변경된 사항을 저장하지 않고, 다른 스레드는 엔티티를 찾을 때 오래된 데이터 . this.getJpaTemplate().flush();의 명시적인 호출 만 도움이되었습니다.

나는 또한 엔티티 관리자가 나에게 영속 컨텍스트의 캐시에서 같은 엔티티 인스턴스를 줄 것이라고 생각했지만 이것은 또한 거짓이다. 나는 hashCode()와 equals()를 체크했다. 엔티티의 bussines 키를 기반으로 괜찮다.

모든 의견을 환영합니다. JPA/Spring이 트랜잭션과 함께 작동하는 방식에 대한 몇 가지 기본 개념을 놓친 것 같습니다.

  1. DAO 방법은 synchronized이어야합니까?
  2. 모든 DAO 메서드의 끝에서 flush()를 호출해야합니까?
  3. DAO 메서드 호출이 트랜잭션으로 동작하도록하는 책임은 스프링입니까? (즉, 동일한 엔티티에서 동시에 두 개의 스레드가 작동하지 않도록)
  4. 그렇지 않은 경우 어떻게해야합니까?

필자의 테스트 케이스에서는 하나의 DAO 객체를 사용하고 있지만 스프링의 bean은 싱글 톤이기 때문에 괜찮을 것입니다. 맞습니까?

도움 주셔서 감사합니다. synchronizedflush()없이

public class EntityDaoImpl extends JpaDaoSupport implements EntityDao { 

public synchronized void incrementCounter(String znacka) 
{ 

    String threadName = Thread.currentThread().getName(); 
    log.info(threadName + " entering do incrementCounter()."); 

    Entity ent = this.getJpaTemplate().find(Entity.class, znacka); 
    log.info("Found an entity "+ent.getZnacka()+"/"+ent.hashCode()+" - " + ObjectUtils.identityToString(ent)); 
    log.info(threadName + ": Actual count: "+ent.getCount()); 

    ent.setCount(ent.getCount() + 5); 

    int sleepTime = threadName.endsWith("A") ? 700 : 50; 
    try { Thread.sleep(sleepTime); } 
    catch(InterruptedException ex) { } 

    ent.setCount(ent.getCount() + 5); 
    this.getJpaTemplate().flush(); 

    log.info(threadName + " leaving incrementCounter()."); 
} 
} 

, 이것은 하나 개의 스레드가 서로의 변경 사항을 덮어 씌우 것을 의미한다, 등등 ...

Thread A: Actual count: 220 
... 
Thread B: Actual count: 220 
... 
Thread A: Actual count: 240 
... 
Thread B: Actual count: 250 
... 
Thread A: Actual count: 250 

같은 출력을 나에게주고 있었다.

답변

3
  1. DAO 방법을 동기화해야합니까? 내 것은 대개 어떤 국가도 없기 때문에 그렇지 않다. 필요 없음.
  2. 모든 DAO 메서드의 끝에서 flush()를 호출해야합니까? 아니, 나는 그렇게하지 않는다.
  3. DAO 메소드에 대한 호출을 처리하는 책임이있는 Spring은 트랜잭션 방식으로 작동합니까? (즉, 동일한 엔티티에서 동시에 두 개의 스레드가 작동하지 않도록). 나는 당신이 트랜잭션, 격리 및 동기화를 혼란스럽게 생각한다고 생각합니다.
  4. 그렇지 않은 경우 어떻게해야합니까? 동기화와 격리에 대해 걱정해야한다고 생각합니다.

귀하의 예는 DAO로 생각하지 않습니다. 나는 당신의 시험이 정말로 적절한 숙어가 아니라고 생각합니다. 당신은 예로부터 추측에는 요로

public interface FooDao 
{ 
    List<Foo> find(); 
    Foo find(Serializable id); 
    void saveOrUpdate(Foo foo); 
    void delete(Foo foo); 
} 

그것은, 일반적인 DAO를 쓰고 자 할 : 나는 객체 푸이 있다면, 그 객체에 대한 CRUD 메소드를 선언 할 것 FooDao 인터페이스이있을 것이다.

스프링에는 일반적으로 사용 사례를 구현하기 위해 도메인 및 지속성 개체를 사용하는 서비스 계층이 있습니다. 작업 단위는 DAO가 아니라 유스 케이스와 관련되어 있기 때문에 트랜잭션을 선언해야합니다. DAO는 더 큰 트랜잭션의 일부인지 여부를 알 수 없습니다. 그래서 거래가 대개 서비스에서 선언됩니다.

+0

답변 해 주셔서 감사합니다. JpaDaoSupport에서 DAO 객체는 참조를 적절한 유형으로 형 변환하는 데 사용되는 얇은 쉘 이었기 때문에 유스 케이스를 DAO에 넣기 시작했습니다. 유스 케이스에 대한 또 다른 레이어를 잘 이해한다면 다른 스프링 관리 빈들로 끝나고, DAO 대신 스프링 트랜잭션으로 AOP 된 유스 케이스를 구현할 것이다. 그래서 그것은 기술적으로 동일 할 것입니다, 그렇지 않습니까? 동기화를 처리하기 위해 어떤 접근 방식을 권하고 싶습니까? Spring 2.5 + JPA 튜토리얼 앱을 추천 해 주시겠습니까? 감사합니다. –

+0

저는 Spring의 관용구를 여러분에게 인용했습니다. 특히 Spring을 처음 사용하는 경우, 다음과 같이하는 것이 좋습니다. 보통 Tomcat, JBOSS 또는 WebLogic과 같은 Java EE 응용 프로그램 서버에서 Spring을 실행하므로 각 요청은 자체 스레드에서 실행됩니다. 앱 서버는 처리 요청을 대기열에 넣습니다. Spring 레퍼런스 문서를 권하고 싶다. – duffymo

+0

글쎄, 내가 뭘하는 웹 애플 리케이션을위한 백엔드도 Tomcat에서 실행됩니다. 스프링 참조는 훌륭하고 방대하지만 동기화에 대한 팁은 아직 포함되어 있지 않습니다. 적어도 찾지 못했습니다. EntityManager # lock (Object entity, LockModeType lockMode)은 내가 살펴 봐야 할 것입니다. 맞습니까? –

1

내가 처리 한 대부분의 응용 프로그램은 별도로, 트랜잭션은 DAO 위의 서비스 계층에서 선언됩니다. 즉, 여러 엔티티에 대해 몇 개의 삽입을 수행하면 트랜잭션 의미론을 가질 수 있습니다. 같은 기업에 경쟁을 많이 기대하는 경우

  • , 당신은 pessimistic locking를 사용할 수있는 또 다른 스레드를 통해 업데이트 할 수 있도록 데이터베이스 잠금을 얻을 것이다 :

    대답은 응용 프로그램에 따라 다릅니다 상단. 동기화 된 DAO와 마찬가지로 이것은 응용 프로그램의 전체 성능을 제한 할 것이며 교착 상태를 피할 수 있도록주의해야합니다

  • 자주 발생하지 않을 것으로 예상되는 경우 낙관적 잠금을 사용하여 버전 열을 유지하고 업데이트 응용 프로그램 코드에서 처리되도록 예외가 throw됩니다.
3

나는, DAO 방법이 거래로 덮여 때, 다음 하나 개의 스레드 만이

당신의 설명을 바탕으로

, incrementCounter 호출 할 때마다 자체 트랜잭션에서 실행해야합니다 내부 될 것이라고 생각 스레드는 동시에 실행되므로 두 개의 트랜잭션이 동시에 실행됩니다.

(즉, 스프링은 동기화를 처리합니다).

동기화와의 트랜잭션이 혼동스러운 것 같습니다. 두 스레드가 자체 트랜잭션을 실행 중이므로 트랜잭션 격리 수준이 SERIALIZABLE로 설정되어 있지 않으면 트랜잭션이 서로에 대해 직렬화됨을 의미하지는 않습니다.

은 또한 엔티티 관리자는 영속 컨텍스트의 범위 내에서, 영속 컨텍스트

네의 캐시에서 나에게 같은 엔티티 인스턴스을 줄 것이라고 생각했다. 지속성 컨텍스트에 트랜잭션 범위가있는 경우 동일한 트랜잭션, 즉 incrementCounter를 한 번 호출 할 때만 동일한 엔티티 인스턴스를 확보 할 수 있습니다.

DAO 방법을 동기화해야합니까?

다른 게시물에서 언급했듯이 일반적으로 DAO 방법은 CRUD 방법이며 DAO는 단순히/업데이트/etc를 선택하기 때문에 일반적으로 동기화하지 않습니다. 당신이하는 일의 더 큰 맥락을 알지 못합니다.

스프링은 DAO 메서드 호출을 트랜잭션 적으로 수행합니까? (즉, 두 엔티티가 동시에 같은 엔티티에서 작동하지 않도록)

다시 트랜잭션! = 동기화.

그렇지 않은 경우 어떻게해야합니까?

다른 게시물에서 언급했듯이 Java 수준에서 동기화를 수행하거나 트랜잭션 격리 수준을 SERIALIZABLE로 설정할 수 있습니다. 또 다른 옵션은 SELECT FOR UPDATE 구문 (예 : 최대 절전 모드에서 LockMode.UPGRADE)을 사용하여 데이터베이스에 변경하려는 것으로 알리고 데이터베이스가 행을 잠궈 야한다는 것입니다.

비관적 잠금에 묶이지 않은 경우 낙관적 잠금을 구현할 수 있습니다 (예 : Hibernate 버저 닝을 사용한다. 특정 incrementCounter 예제를 사용하면 많은 낙관적 인 잠금 실패가 발생하지만 실제 응용 프로그램은 이와 같이 동작하지 않는다고 가정합니다.

관련 문제