2013-05-06 3 views
0

Я использую JPA в настольном приложении Swing. Это то, что мой код выглядит следующим образом:Как сделать окончательно в 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;  
} 

Я использую этот try - catch - finally для всех методов, который требует операции. Он работает как ожидалось, за исключением методов, которые имеют SwingUtilities.invokeLater().

finally будет достигнут прежде, чем будет выполнен весь код в новой цепочке; таким образом, если есть операция JPA внутри SwingUtilities.invokeLater(), она не сработает, потому что транзакция была совершена.

Есть ли общий вариант использования try - catch - finally, который я могу использовать, чтобы убедиться, что транзакция будет совершена только после того, как весь код будет выполнен, включая код внутри SwingUtilities.invokeLater()?

+1

Почему вы манипулируете сущностями, используя 'SwingUtilities.invokeLater'? точка этого метода заключается в обновлении gui _after_, когда вы выполняете работу по обновлению. – jtahlborn

+0

Боюсь, что в один прекрасный день я неожиданно запустил бы ленивый выбор. – jocki

+1

Lazy fetching - ваша настоящая проблема, поэтому работайте над ее решением. Ваш код, как написано, является * полной противоположностью * того, как вы должны работать с Swing. И так как поток EDT * никогда не заканчивается *, пока ваша программа не выйдет, ваше основное предположение о том, как должен выполняться ваш код, неверно. – kdgregory

ответ

1

Вы должны пересмотреть свой подход. Прежде всего SwingUtilities.invokeLater() не лучший выбор для выполнения операции JPA. Основная цель этого метода утилиты - обновить интерфейс. Что касается вашего кода, реализуйте отдельный поток для операций JPA, этот поток будет принимать слушателя статуса транзакции. Вы обновите пользовательский интерфейс после завершения транзакции.

/** 
* 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

Если я выполняю транзакцию и закрываю 'EntityManager' в' JPATask', будет 'LazyInitializationException'. Я знаю, что здесь не должно быть длительной работы SQL, но иногда, когда я передаю сущности в 'TableModel' или' ComboBoxModel', их рендеринг неожиданно вызывает ленивую выборку. – jocki

+1

Это потому, что вы касаетесь _lazy fields_ из транзакции. Самый простой способ исправить это - просто использовать параметр [FetchType.EAGER] (http://docs.oracle.com/javaee/6/api/javax/persistence/FetchType.html) в аннотации для соответствующих полей. – Eugene

Смежные вопросы