【问题标题】:How to make finally in try-finally wait for threads to finish?如何在try-finally中使finally等待线程完成?
【发布时间】:2013-05-06 17:31:46
【问题描述】:

我在基于 Swing 的桌面应用程序中使用 JPA。这是我的代码的样子:

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 将在新线程中的所有代码执行之前到达;因此,如果SwingUtilities.invokeLater()内部有JPA操作,则会因为事务已经提交而失败。

我是否可以遵循try - catch - finally 的通用用例以确保仅在执行所有代码(包括SwingUtilities.invokeLater() 中的代码)后才会提交事务?

【问题讨论】:

  • 你为什么要使用SwingUtilities.invokeLater来操作实体?该方法的重点是在您完成更新工作之后更新 gui
  • 我怕有一天我会在那里意外触发偷取。
  • 延迟抓取是你真正的问题,所以努力解决它。您编写的代码与您应该使用 Swing 的方式完全相反。而且由于 EDT 线程永远不会完成直到您的程序退出,您对代码应该如何执行的基本假设是不正确的。
  • 我打算使用 AOP 将这些 try-catch-finally 注入所有相关方法。我将尝试通过尽可能长时间保持 EntityManager 打开来解决延迟获取问题。即使代码不在事务内部(也许这不在 JPA 中?),Hibernate 也会执行延迟获取(延迟加载)。这样一来,延迟获取将在 SwingUtilities.invokeLater 和 EDT 中工作。

标签: java jpa jpa-2.0 try-catch-finally try-finally


【解决方案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();     
}

【讨论】:

  • 如果我正在执行事务提交并关闭JPATask中的EntityManager,就会有LazyInitializationException。我知道这里不应该有冗长的 SQL 操作,但有时当我将实体传递给 TableModelComboBoxModel 时,它们的渲染器会意外导致执行延迟提取。
  • 那是因为你在事务之外触摸了惰性字段。解决此问题的最简单方法是在注释中为适当的字段使用 FetchType.EAGER 参数。
猜你喜欢
  • 2014-09-12
  • 2018-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-05
  • 2012-01-24
  • 2018-02-24
  • 2014-11-27
相关资源
最近更新 更多