/**
   * Run a block of code in a JPA transaction.
   *
   * @param name The persistence unit name
   * @param readOnly Is the transaction read-only?
   * @param block Block of code to execute
   */
  public <T> T withTransaction(String name, boolean readOnly, Supplier<T> block) {
    EntityManager entityManager = null;
    EntityTransaction tx = null;

    try {
      entityManager = em(name);

      if (entityManager == null) {
        throw new RuntimeException("No JPA entity manager defined for '" + name + "'");
      }

      JPA.bindForSync(entityManager);

      if (!readOnly) {
        tx = entityManager.getTransaction();
        tx.begin();
      }

      T result = block.get();

      if (tx != null) {
        if (tx.getRollbackOnly()) {
          tx.rollback();
        } else {
          tx.commit();
        }
      }

      return result;

    } catch (Throwable t) {
      if (tx != null) {
        try {
          tx.rollback();
        } catch (Throwable e) {
        }
      }
      throw t;
    } finally {
      JPA.bindForSync(null);
      if (entityManager != null) {
        entityManager.close();
      }
    }
  }
  /**
   * Run a block of asynchronous code in a JPA transaction.
   *
   * @param name The persistence unit name
   * @param readOnly Is the transaction read-only?
   * @param block Block of code to execute.
   * @deprecated This may cause deadlocks
   */
  @Deprecated
  public <T> F.Promise<T> withTransactionAsync(
      String name, boolean readOnly, Supplier<F.Promise<T>> block) {
    EntityManager entityManager = null;
    EntityTransaction tx = null;

    try {
      entityManager = em(name);

      if (entityManager == null) {
        throw new RuntimeException("No JPA entity manager defined for '" + name + "'");
      }

      JPA.bindForAsync(entityManager);

      if (!readOnly) {
        tx = entityManager.getTransaction();
        tx.begin();
      }

      F.Promise<T> result = block.get();

      final EntityManager fem = entityManager;
      final EntityTransaction ftx = tx;

      F.Promise<T> committedResult =
          (ftx == null)
              ? result
              : result.map(
                  t -> {
                    if (ftx.getRollbackOnly()) {
                      ftx.rollback();
                    } else {
                      ftx.commit();
                    }
                    return t;
                  });

      committedResult.onFailure(
          t -> {
            if (ftx != null) {
              try {
                if (ftx.isActive()) {
                  ftx.rollback();
                }
              } catch (Throwable e) {
              }
            }
            try {
              fem.close();
            } finally {
              JPA.bindForAsync(null);
            }
          });
      committedResult.onRedeem(
          t -> {
            try {
              fem.close();
            } finally {
              JPA.bindForAsync(null);
            }
          });

      return committedResult;

    } catch (Throwable t) {
      if (tx != null) {
        try {
          tx.rollback();
        } catch (Throwable e) {
        }
      }
      if (entityManager != null) {
        try {
          entityManager.close();
        } finally {
          JPA.bindForAsync(null);
        }
      }
      throw t;
    }
  }