web-dev-qa-db-ja.com

JPAとJava 8のベストプラクティス

Java8のセマンティクスが大好きです。私はDAOでそのようなコードをたくさん使用しています:

  public Optional<User> findBy(String username) {
    try {
      return Optional.of(
        emp.get().createQuery("select u from User u where u.username = :username" , User.class)
        .setParameter("username" , username)
        .setMaxResults(1)
        .getSingleResult()
      );
    } catch (NoResultException e) {
      return Optional.empty();
    }
  }

それはうまく機能しますが、そのようなコード(NoResultExceptionをキャッチしてみてください)は私のDAOに散らばっています。また、例外をキャッチする必要があるため、パフォーマンスが低下します。

それが最善の解決策ですか?またはtry-catchなしのより良い解決策?

それが不可能な場合(NoResultExceptionがJPAで定義されているため)、そのようなワークフローを「テンプレート化」するためのショートカットはありますか?

ありがとう。

15
smallufo

もちろん、ラムダの魔法を使って、それをテンプレート化できます!

@FunctionalInterfaceラムダのコントラクトを定義するには:

@FunctionalInterface
public interface DaoRetriever<T> {
    T retrieve() throws NoResultException;
}

これは、メソッドの動作をカプセル化する単一メソッドインターフェイス(SMI)です。

次に、SMIを使用するユーティリティメソッドを作成します。

public static <T> Optional<T> findOrEmpty(final DaoRetriever<T> retriever) {
    try {
        return Optional.of(retriever.retrieve());
    } catch (NoResultException ex) {
        //log
    }
    return Optional.empty();
}

さて、import static呼び出しコードでは、上記のメソッドは次のようになります。

public Optional<User> findBy(String username) {
    return findOrEmpty(() ->
            emp.get().createQuery("select u from User u where u.username = :username", User.class)
                    .setParameter("username", username)
                    .setMaxResults(1)
                    .getSingleResult());
}

だからここ、() -> emp.get()...は、検索動作をキャプチャするラムダです。 interface DaoRetrieverNoResultExceptionをスローできるため、ラムダもスローされます。

または、TypedQuery-getResultList-の他の方法を使用して、コードを次のように変更します。

public Optional<User> findBy(String username) {
    return emp.get().createQuery("select u from User u where u.username = :username", User.class)
            .setParameter("username", username)
            .setMaxResults(1)
            .getResultList()
            .stream()
            .findFirst();
}

これには、より単純であるという利点がありますが、他の結果があればそれを単に破棄するという欠点があります。

26

ボリスは正しい方向に進んでいますが、もっとうまくやることができます。さらに抽象化が必要です。この変換はdaosとは関係ありません。

例外をスローするラムダをスローしないラムダに変換する、さまざまなアリティのファミリーまたは機能インターフェイスが必要です。 FunctionalJava( http://www.functionaljava.org/ )はこれを行います:

したがって、Tryクラスのファミリーがあります:Try0、Try1など。

public interface Try0<A, Z extends Exception> {
    A f() throws Z;
}

そして、それを例外をスローしない関数に変換したいと思います。

static public <A, E extends Exception> Supplier<Validation<E, B>> toSupplierValidation(final Try0<A, E> t) {
    return () -> {
        try {
            return Validation.success(t.f());
        } catch (Exception e) {
            return Validation.fail((E) e);
        }
    };
}

検証は、失敗した場合の例外、または成功した場合の通常の値のいずれかであることに注意してください( https://functionaljava.ci.cloudbees.com/job/master/javadoc/ )。例外を気にしない場合は、失敗のケースを空のオプションに変換し、成功のケースをオプションの値にすることができます。このメソッドはBorisのメソッドに似ていますが、daoの参照(無関係)はありません。

static public <A, E extends Exception> Supplier<Optional<A>> toSupplierOptional(final Try0<A, E> t) {
    return () -> {
        try {
            return Optional.of(t.f());
        } catch (Exception e) {
            return Optional.empty();
        }
    };
}
3
Mark Perry