2013-05-06 3 views
0

저는 Swing 기반 데스크톱 응용 프로그램에서 JPA를 사용하고 있습니다. 내가 거래를 필요로하는 모든 방법이 try - catch - finally을 사용하고마침내 try-finally에서 마침내 스레드가 완료 될 때까지 기다리는 방법?

public Object methodA() { 

    EntityManager em = emf.createEntityManager(); 
    em.getTransaction().begin(); 
    boolean hasError = false; 

    try { 
     SwingUtilities.invokeLater(new Runnable() { 
      public void run() { 
       // JPA operation does not work here 
       // because transaction has been committed! 
      } 
     }); 
     ... 
     return xXx; 
    } catch (Exception ex) { 
     hasError = true; 
     em.getTransaction().rollback(); 
    } finally { 
     em.close(); 
     if (!hasError) { 
      em.getTransaction().commit(); 
     } 
    } 
    return null;  
} 

:이처럼 내 코드는 모습입니다. SwingUtilities.invokeLater() 메서드를 제외하고 예상대로 작동합니다.

finally은 새 스레드의 모든 코드가 실행되기 전에 도달합니다. 따라서 SwingUtilities.invokeLater() 안에 JPA 작업이 있으면 트랜잭션이 커밋되었으므로 실패합니다.

try - catch - finally의 일반적인 사용 사례는 SwingUtilities.invokeLater() 내부의 코드를 포함하여 모든 코드가 실행 된 후에 만 ​​트랜잭션이 커밋된다는 것을 확인할 수 있습니까?

+1

왜 'SwingUtilities.invokeLater'를 사용하여 엔티티를 조작하고 있습니까? 이 방법의 핵심은 업데이트 작업을 완료 한 후 GUI를 업데이트하는 것입니다. – jtahlborn

+0

언젠가는 내가 예기치 않게 게으른 페치를 유발할까 봐 걱정됩니다. – jocki

+1

게으른 인출이 실제 문제이므로이를 해결하십시오. 쓰여진 코드는 Swing을 사용하는 방법과 정반대입니다. 그리고 프로그램이 종료 될 때까지 EDT 스레드 *가 끝나지 않기 때문에 코드가 어떻게 실행되어야하는지에 대한 기본 가정이 올바르지 않습니다. – kdgregory

답변

1

당신의 접근 방식에 대해 다시 생각해 봐야합니다. 우선 SwingUtilities.invokeLater()은 JPA 작업 수행을위한 최상의 선택이 아닙니다. 이 유틸리티 메소드의 주요 목적은 UI 업데이트입니다. 코드와 관련하여 JPA 작업에 대해 별도의 스레드를 구현하면이 스레드는 트랜잭션 상태를 수신합니다. 트랜잭션이 완료되면 UI를 업데이트합니다.

/** 
* Transaction callback. 
*/ 
public interface TransactionListener { 
    void onTransactionFinished(boolean hasError); 
} 

/** 
* Worker Thread which takes data and performs JPA operations. 
*/ 
public class JPATask implements Runnable { 
    private final Object dataToPersist; 
    private final TransactionListener transactionListener; 

    public JPATask(Object dataToPersist, 
      TransactionListener transactionListener) { 
     this.dataToPersist = dataToPersist; 
     this.transactionListener = transactionListener; 
    } 

    private EntityManager getEntityManager() {/* code is omited */} 

    @Override 
    public void run() { 
     EntityManager em = getEntityManager(); 
     try { 
      em.getTransaction().begin(); 
      // perform JPA actions here 
      em.getTransaction().commit(); 
      transactionListener.onTransactionFinished(false); 
     } catch (Exception ex) { 
      em.getTransaction().rollback(); 
      transactionListener.onTransactionFinished(true); 
     } 
    } 
} 

/** 
* Finally you method. Now it looks like this. 
*/ 
public Object methodA() { 
    JPATask jpaTask = new JPATask(<data to persist>, new TransactionListener() { 
     @Override 
     public void onTransactionFinished(boolean hasError) { 
      // Update UI. It's time to use SwingUtilities.invokeLater() 
     } 
    }).start();  
} 
+0

트랜잭션 커밋을하고'JPATask'에서'EntityManager'를 닫으면'LazyInitializationException'이 생깁니다. 여기에 SQL 연산이 길어서는 안되지만, 때때로 엔티티를'TableModel' 또는'ComboBoxModel'에 전달할 때 렌더러가 예기치 않게 지연 페칭을 수행하게됩니다. – jocki

+1

거래가 _ 지연 _ 필드를 터치하기 때문입니다. 이 문제를 해결하는 가장 간단한 방법은 주석에 적절한 필드에 대한 [FetchType.EAGER] (http://docs.oracle.com/javaee/6/api/javax/persistence/FetchType.html) 매개 변수를 사용하는 것입니다. – Eugene

관련 문제