2016-07-15 2 views
2

교착 상태 문제가 있으며 어떻게 처리 할 수 ​​있는지 알 수 없습니다. glassfish 서버에서 JPA 2.1 (eclipselink 포함)을 사용하고 있습니다.@TransactionAttribute (TransactionAttributeType.REQUIRES_NEW)로 JPA 교착 상태가 발생했습니다. 내 애플리케이션에

두 개의 EJB가 있습니다. OuterBeanLogEntry을 데이터베이스에 쓰고 루프에서 InnerBean을 호출해야합니다. InnerBean 그 자체 은 LogEntry을 쓰고 다른 것들 (다른 엔티티에서 더 많은 데이터베이스 조작)을해야합니다. InnerBean#execute()의 호출은 서로 독립적이므로 한 방법이 실패하면 (롤백) 나머지는 계속 실행해야합니다. 따라서 InnerBean#execute()은 자체 트랜잭션으로 실행됩니다.

내가 MySQL을 아래 java.sql.SQLException: Lock wait timeout exeeded; try restarting transaction 오라클 아래 java.sql.SQLSyntaxErrorException: ORA-02049: timeout: distributed transaction waiting for lock 를 얻을 수 아래의 코드를 실행

(포스트 그레스 그냥 영원히 대기, 아마 때문에 나쁜 구성 데이터베이스).

저는 데이터베이스/JPA 전문가가 아니지만 두 트랜잭션이 동일한 데이터 테이블에 쓰기를 원한다고 생각합니다. 내가하지 않는 것 이 데이터베이스 조작은 각각 다른 기타와 독립적이어야하는 삽입이라는 점에서 문제가 있다는 것을 이해합니다. 이 유스 케이스를 구현할 수있는 방법이 있습니까 (Bean 관리 트랜잭션을 사용해야합니까, 사용할 수있는 주석이 있습니까, em.persist (logEntry) 이후에 OuterBean#execute()에 커밋을 적용 할 수 있습니까?) 트랜잭션 잠금은 아무렇게나 해제됩니다)? 여기


EDIT

@Stateless 
public class InnerBean 
{ 
    @PersistenceContext(unitName = "PU_LOGGER") 
    private EntityManager em; 

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 
    public void execute() 
    { 
     LogEntry logEntry = new LogEntry(); 
     logEntry.setDate(new Date()); 
     logEntry.setMessage("InnerBean#execute()" + Math.random()); 
     em.persist(logEntry); 
    } 
} 

@Stateless 
public class OuterBean 
{ 
    @PersistenceContext(unitName = "PU_LOGGER") 
    private EntityManager em; 
    @EJB 
    private InnerBean innerBean; 

    public void execute() 
    { 
     LogEntry logEntry = new LogEntry(); 
     logEntry.setDate(new Date()); 
     logEntry.setMessage("OuterBean#execute()"); 
     em.persist(logEntry); 

     for(int i = 0; i < 10; ++i) 
     { 
      innerBean.execute(); 
     } 
    } 
} 
는 I 문제점을 해결 한

FINER: client acquired: 1990452734 
FINER: TX binding to tx mgr, status=STATUS_ACTIVE 
FINER: acquire unit of work: 38847372 
FINEST: persist() operation called on: LogEntry{id=null, message=OuterBean#execute(), date=Mon Jul 18 09:00:27 CEST 2016}. 
FINER: TX beginTransaction, status=STATUS_ACTIVE 
FINEST: Connection acquired from connection pool [default]. 
FINEST: Execute query DataModifyQuery(name="SEQUENCE" sql="UPDATE SEQUENCE SET SEQ_COUNT = SEQ_COUNT + #PREALLOC_SIZE WHERE SEQ_NAME = #SEQ_NAME") 
FINEST: reconnecting to external connection pool 
FINE: UPDATE SEQUENCE SET SEQ_COUNT = SEQ_COUNT + ? WHERE SEQ_NAME = ? 
    bind => [2 parameters bound] 
FINEST: Execute query ValueReadQuery(name="SEQUENCE" sql="SELECT SEQ_COUNT FROM SEQUENCE WHERE SEQ_NAME = #SEQ_NAME") 
FINE: SELECT SEQ_COUNT FROM SEQUENCE WHERE SEQ_NAME = ? 
    bind => [1 parameter bound] 
FINEST: local sequencing preallocation for SEQ_GEN: objects: 50 , first: 301, last: 350 
FINEST: assign sequence to the object (301 -> LogEntry{id=null, message=OuterBean#execute(), date=Mon Jul 18 09:00:27 CEST 2016}) 
FINER: client acquired: 187184807 
FINER: TX binding to tx mgr, status=STATUS_ACTIVE 
FINER: acquire unit of work: 2098992041 
FINEST: persist() operation called on: LogEntry{id=null, message=InnerBean#execute()0.3957184758563761, date=Mon Jul 18 09:00:28 CEST 2016}. 
FINER: TX beginTransaction, status=STATUS_ACTIVE 
FINEST: Connection acquired from connection pool [default]. 
FINEST: Execute query DataModifyQuery(name="SEQUENCE" sql="UPDATE SEQUENCE SET SEQ_COUNT = SEQ_COUNT + ? WHERE SEQ_NAME = ?") 
FINEST: reconnecting to external connection pool 
FINE: UPDATE SEQUENCE SET SEQ_COUNT = SEQ_COUNT + ? WHERE SEQ_NAME = ? 
    bind => [2 parameters bound] 
+0

응용 프로그램이나 엔터티에서 어떤 잠금을 수행합니까? 컨테이너가 실제로 새 트랜잭션을 시작했는지 또는 주석을 무시하고 메소드가 더 큰 트랜잭션 컨텍스트로 래핑되었는지 확인할 수 있습니까? 로깅을 켜서 최고 수준으로 진행하고 진행 상황을 확인하십시오. – Chris

+0

안녕하세요, @ 크리스, 생성 된 가장 멋진 로그를 추가했습니다. 나는 모든 것을 이해할 수는 없지만 두 가지 거래가 시작될 것임을 알았습니다. 만약 InnerBean에'@TransactionAttribute (REQUIRES_NEW)'주석을 남겨두면 모든 것이 잘 동작한다는 것이 제 견해에 부합 할 것입니다. 잠금과 관련해서는 명시 적 잠금을 수행하지 않습니다. 위의 코드는 CDI 빈을 통해 실행될 것이고 아무 것도 잠그지 않는 코드는 더 이상 존재하지 않습니다. – Filou

+0

시퀀스를위한 연결 풀을 구성해야하므로 JTA 트랜잭션 외부에서 시퀀스 테이블의 시퀀스 번호를 얻거나 할당 할 수 있습니다. http://www.eclipse.org/eclipselink/documentation/2.4/concepts/data_access006.htm#CHDEFJHH 및 http://stackoverflow.com/questions/19732551/how-to-configure-an-eclipselink-jta-sequence를 참조하십시오. 비 -JTA 데이터 소스를 지정하는 방법을 보여주는 -connection-pool – Chris

답변

1

OK 생성 된 로그이다. 그것에 대한 대답은 내 질문에 숨겨져 :

가 나는이 em.persist (logEntry) 후) (실행 OuterBean # 커밋 강제 수있다; 그리고 루프 이전

나는 LogEntry를 영속화하고 새로운 트랜잭션 자체에서 그것을 실행하기 위해서만 별도의 EJB를 생성했다.

@Stateless 
public class SeparateLoggingBean 
{ 
    @PersistenceContext(unitName = "PU_LOGGER") 
    private EntityManager em; 

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 
    public void persistLogEntry(LogEntry logEntry) 
    { 
     em.persist(logEntry); 
    } 
} 

@Stateless 
public class OuterBean 
{ 

    @EJB 
    private SeparateLoggingBean separateLoggingBean; 

    @EJB 
    private InnerBean innerBean; 


    public void execute() 
    { 
     LogEntry logEntry = new LogEntry(); 
     logEntry.setDate(new Date()); 
     logEntry.setMessage("OuterBean#execute()"); 
     separateLoggingBean.persistLogEntry(logEntry); 

     for(int i = 0; i < 10; ++i) 
     { 
      innerBean.execute(); 
     } 
     System.out.println("!ready!"); 
    } 
} 

어쨌든 나는 @ 크리스가 자신의 두 번째 의견에 권리라고 생각합니다. 문제는 eclipselink 시퀀스 생성에서 교착 상태 인 것 같습니다. 해결 방법이있는 것 같습니다 ( http://www.eclipse.org/eclipselink/documentation/2.6/jpa/extensions/persistenceproperties_ref.htm#BABIDAGH). 제대로 작동하지는 않습니다. 언급 된 링크에서 재산

<property name="eclipselink.connection-pool.sequence" value="true"/> 

가의 persistence.xml에 넣어 될 필요가 있다고 말한다. 불행히도 내 테스트에서 나는 그 재산을 사용했는지 여부를 전혀 바꾸지 않았다. 이 잘 알려진 문제에 대한 몇 가지 예를 발견했습니다.오래된 구식 속성은 현재의 (2.6) 문서에서 찾지 못했던 구형 eclipselink 버전에서 종종 사용되는 경우가 있습니다. 별도의 비 jta 연결 풀을 지정해야하거나 eclipselink.connection-pool.sequence 속성을 지정하면 eclipselink가 풀을 자체적으로 관리하는지 여부가 명확하지 않습니다.

이것에 대한 추가 정보를 얻는 것이 좋겠지 만, 적어도 내 응용 프로그램이 작동한다는 것이 기쁩니다.