2013-04-02 3 views
0

최대 절전 모드에서 EJB 3을 사용하고 있습니다. 나는 스테이트리스 세션 Bean을 가지고있다. 해당 빈에 deleteItem 메소드가 있습니다.최대 절전 모드 OptimisticLockException 캐치 실패

클라이언트가 deleteItem 메소드를 호출하면 문제없이 삭제가 발생합니다. 하지만 for 루프를 사용하여 deleteItem 메서드를 호출하고 해당 루프의 한계를 5-10 번 설정하려고하면 삭제가 실패하는 경우가 있습니다. 하지만 항상 그런 것은 아닙니다.

삭제 작업은 실제로 2 개의 테이블에서 데이터를 삭제합니다. 자식 테이블과 부모 테이블 각 삭제는 플러시 작업을 수행하여 커밋됩니다.

내가 이미 하나씩 삭제를 실행하면 문제가 발생하지 않는다는 것을 이미 언급했듯이, 동시에 실행하려고 할 때만 발생합니다. 내가 받고있는 예외는 다음과 같습니다

Caused by: java.sql.BatchUpdateException: Cannot delete or update a parent row: a foreign key 
constraint fails (`functionTest/MotherTable`, CONSTRAINT `FKBC6CB0E6D5545EFD` FOREIGN KEY 
(`MotherTable_FieldId`) REFERENCES `ChildTable` (`childTableId`)) 

여기서 동시 삭제 작업을 수행 할 방법이 없습니다. 그리고 항목 삭제는 다른 항목 삭제 작업과 관련이 없습니다. 동시성이 여전히 발생하면 동시에 여러 항목을 삭제해도 문제가되지 않습니다.

따라서 "클라이언트가 여러 스레드에서 동일한 Bean 인스턴스에 액세스하고있을 수 있습니다."라는 결론이났습니다. 이러한 상황에서 두 스레드는 동일한 Entity Manager State를 다른 상태로 유지합니다. 하나는 아직 다른 항목이 자식 항목 제거가 완료되지 않은 경우 지속성 컨텍스트를 플러시하려고 시도합니다. 그 시점에서 BatchUpdateException이 발생했습니다. - 내 관찰이야. 나는 100 % 확실하지 않다.

이 상황을 극복하기 위해 낙관적 잠금을 사용했습니다. 내가 어머니 테이블에 버전 열을 만들었습니다. 이제 OptimisticLockException이 발생합니다. 그러나 나는 예외를 잡을 수 없다. 아래는 OptimisticLockException을 잡기 위해 사용하는 코드입니다.

private boolean deleteItem(Item itemId) { 

      Item item= getItem(itemId); 
     removeChildTableData(item); 
     mEm.remove(item); 

    try 
      { 
     mEm.flush(); 
    } 
    catch (OptimisticLockException e) 
      { 
     try { 
      Thread.sleep(1000); 
      } 
        catch (InterruptedException e1) { 
      e1.printStackTrace(); 
     } 
     deleteItem(itemId); 

     } 
    catch(Exception ex) 
       { 

     if (ex.getCause() instanceof OptimisticLockException) 
        { 
         try { 
          Thread.sleep(1000); 
          } catch (InterruptedException x) { 
          } 
        deleteItem(itemId); 

        } 


     } 

    return true; 
    } 

내 목표는 OptimisticLockException을 잡아서 다시 삭제 작업을 다시 실행하는 것입니다. 예외 클래스 이름을 확인했으며 EntityNotFound입니다. 하지만 stacktrace에서 StaticObjectStateException뿐만 아니라 OptimisticLockException을 얻고 있음을 알 수 있습니다.

그럼, 아무도 내게이 OptimisticLockException을 잡아 야하는 방법을 안내해 줄 수 있습니까?

+0

중요한 점은 어디에서 예외가 발생하는지 전체 스택 추적이 많은 도움이 될 것이라고 생각합니다. – wildhemp

+1

오류 메시지의 내용을 신뢰할 수 있어야합니다. 외래 키 제약 조건이 깨 졌다고하면 외래 키 제약 조건이 깨졌습니다. 낙관적 인 사랑을 추가해도 아무런 변화가 없습니다. 그리고 낙관적 인 잠금 예외를 잡아서 같은 세션과 엔티티를 계속 사용해서는 안됩니다. 그것은 작동하지 않습니다. http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html_single/#transactions-demarcation-exceptions을 읽으십시오. –

+0

@JBNizet - 귀하의 요지를 이해했습니다. 그러나 문제는 그것이 항상 일어나지 않는다는 것입니다. 동시성이 발생할 때만 발생합니다. 그리고 네가 맞습니다. 낙관적 인 락 예외를 처리 한 후에 새로운 트랜잭션에서 실행해야합니다. – ifti24

답변

0

하지 않아야합니다. 또한 JB가 말한 것에 +1. 이 예외는 당신에게 뭔가를 말하려고합니다. 자식이 여전히 참조하는 동안 외래 키 관계의 부모 행을 삭제하려고합니다. 부모와 자녀는 무엇입니까? 음 :

a foreign key constraint fails (`functionTest/MotherTable`, CONSTRAINT `FKBC6CB0E6D5545EFD` FOREIGN KEY (`MotherTable_FieldId`) REFERENCES `ChildTable` (`childTableId`)) 

그래서 MotherTable.MotherTableFieldId가 ChildTable.childTableId를 참조하고 있습니다. 그리고 그 아이의 엄마가 아직 아이를 가리키고있는 동안 아이를 지우려고합니다. 그것은 작동하지 않습니다.

왜 이런 방식으로 관계를 맺을 수 있는지 궁금합니다. 귀하의 모델은 다음과 같이 보입니다 :

@Entity 
@Table(name="MotherTable") 
class Mother { 
    @Id 
    Long id; 
    @ManyToOne 
    @JoinColumn(name="MotherTable_FieldId") 
    Child child; 
} 

@Entity 
@Table(name="ChildTable" 
class Child { 
    @Id 
    @Column(name="childTableId") 
    Long id; 
    @OneToMany(mappedBy="child") 
    Set<Mother> mothers; 
} 

자녀가 많은 엄마를 가질 수 있기 때문에 이상합니다.어쩌면 당신은 대신를 원 :

이 경우
@Entity 
class Mother { 
    @Id 
    Long id; 
    @OneToMany(mappedBy="mother") 
    Set<Child> children; 
} 

@Entity 
class Child { 
    @Id 
    Long id; 
    @ManyToOne 
    @JoinColumn(name="mother_id") 
    Mother mother; 
} 

, 당신의 DAO 방법은 다음과 같을 것이다 :

@Transactional 
public void deleteFamily(Mother mother) { 
    for (Child c: mother.getChildren()) { 
    em.remove(c); 
    } 
    em.remove(mother); 
} 
또한 계단식 사용할 수

:

@Entity 
class Mother { 
    @Id 
    Long id; 
    @OneToMany(mappedBy="mother", cascading=CascadeType.ALL) 
    Set<Child> children; 
} 

DAO를 방법을 단순화 ~까지 :

@Transactional 
public void deleteFamily(Mother mother) { 
    em.remove(mother); 
} 

그리고 심지어 :

@Entity 
class Mother { 
    @Id 
    Long id; 
    @OneToMany(mappedBy="mother", cascading=CascadeType.ALL, orphanRemoval=true) 
    Set<Child> children; 
} 

지금 당신이 em.remove() 아이들에게하지 않습니다, 당신은 em.flush()과의 거래를 저지하려고해서는 안 또한

@Transactional 
public void deleteChild(Child child) { 
    Mother m = child.getMother(); 
    m.getChildren().remove(child); 
} 

, 그 몇 가지 계산에 잘못 :

  1. 거래가 em.getTransaction().commit()
  2. 로 처리됩니다. 수행하려고하는 것에 대해 생각해보십시오. 한 거래에서 패밀리가 일어날 거라 생각해? 예? 그런 식으로 구현하십시오. 자식을 삭제 한 후에 부분 커밋을 시도하지 마십시오.
  3. 누군가 다른 사람이 거래를 관리하도록하는 것이 훨씬 편리합니다. 메소드를 @Transactional로 표시하고 JTA 프레임 워크가 세부 사항을 처리하게하십시오.
  4. 그리고 DAO 방법은 어쨌든 트랜잭션을 시도하지 않아야합니다. 이것에 대해 생각해보십시오. 나중에 여러 DAO 메소드를 사용하는 서비스를 구현하고자 할 수 있습니다. 각자가 서로 다른 트랜잭션으로 커밋하려고하면 서비스 호출은 트랜잭션이 될 수 없습니다. 그 나쁜. 따라서 DAO 메소드를 재사용하려면 트랜잭션 항목을 그 위에있는 별도의 레이어로 가져옵니다.
관련 문제