1概要

データアクセスオブジェクト(DAO)パターンは、抽象APIを使用して** アプリケーション/ビジネス層を永続層(通常はリレーショナルデータベースですが、他の永続メカニズムでも構いません)から分離することを可能にする構造パターンです。

このAPIの機能は、基礎となるストレージメカニズムでCRUD操作を実行することに関連するすべての複雑さをアプリケーションから隠すことです。これにより、互いについて何も知らずに両方の層を別々に進化させることができます。

このチュートリアルでは、パターンの実装について深く掘り下げ、https://docs.oracle.com/javaee/7/api/javax/persistence/への呼び出しを抽象化するための使用方法を学びます。 EntityManager.html[JPAエンティティマネージャ]


2簡単な実装

DAOパターンがどのように機能するかを理解するために、基本的な例を作成しましょう。

ユーザーを管理するアプリケーションを開発したいとしましょう。アプリケーションのドメインモデルがデータベースを完全に認識しないようにするために、これらのコンポーネントを互いにきちんと切り離しておくようにするための簡単なDAOクラスを作成します。

** 2.1. ドメインクラス

**

私たちのアプリケーションはユーザーと連携するので、そのドメインモデルを実装するためのクラスを1つだけ定義する必要があります。

public class User {

    private String name;
    private String email;

   //constructors/standard setters/getters
}


User

クラスは、ユーザーデータを格納するための単なるコンテナーです。そのため、強調する価値のあるその他の動作は実装されていません。

もちろん、ここで行う必要がある最も関連性のある設計上の選択は、このクラスを使用するアプリケーションを、ある時点で実装できる永続化メカニズムから分離する方法です。

まあ、それこそまさにDAOパターンが取り組もうとしている問題です。


2.2. DAO API

基本的なDAO層を定義しましょう。そうすれば、ドメインモデルを永続層から完全に切り離すことができるかどうかがわかります。

これがDAO APIです。

public interface Dao<T> {

    Optional<T> get(long id);

    List<T> getAll();

    void save(T t);

    void update(T t, String[]params);

    void delete(T t);
}

鳥瞰図から、

Dao

インターフェースが

T

型のオブジェクトに対してCRUD操作を実行する抽象APIを定義していることは明らかです。

インターフェースが提供する抽象度が高いため、

User

オブジェクトを扱う具体的できめ細かい実装を簡単に作成できます。


2.3.

UserDao

クラス


Dao

インターフェースのユーザー固有の実装を定義しましょう。

public class UserDao implements Dao<User> {

    private List<User> users = new ArrayList<>();

    public UserDao() {
        users.add(new User("John", "[email protected]"));
        users.add(new User("Susan", "[email protected]"));
    }

    @Override
    public Optional<User> get(long id) {
        return Optional.ofNullable(users.get((int) id));
    }

    @Override
    public List<User> getAll() {
        return users;
    }

    @Override
    public void save(User user) {
        users.add(user);
    }

    @Override
    public void update(User user, String[]params) {
        user.setName(Objects.requireNonNull(
          params[0], "Name cannot be null"));
        user.setEmail(Objects.requireNonNull(
          params[1], "Email cannot be null"));

        users.add(user);
    }

    @Override
    public void delete(User user) {
        users.remove(user);
    }
}


UserDao

クラスは、

User

オブジェクトの取得、更新、および削除に必要なすべての機能を実装しています。

  • わかりやすくするために、

    users List

    は、インコンストラクタ内にいくつかの

    User

    オブジェクトが格納されたインメモリデータベースのように機能します** 。

もちろん、他の方法をリファクタリングするのは簡単なので、たとえばリレーショナルデータベースを使って作業することができます。


User

クラスと

UserDao

クラスは同じアプリケーション内で独立して共存しますが、永続層をアプリケーションロジックから隠すために後者をどのように使用できるかを確認する必要があります。

public class UserApplication {

    private static Dao userDao;

    public static void main(String[]args) {
        userDao = new UserDao();

        User user1 = getUser(0);
        System.out.println(user1);
        userDao.update(user1, new String[]{"Jake", "[email protected]"});

        User user2 = getUser(1);
        userDao.delete(user2);
        userDao.save(new User("Julie", "[email protected]"));

        userDao.getAll().forEach(user -> System.out.println(user.getName()));
    }

    private static User getUser(long id) {
        Optional<User> user = userDao.get(id);

        return user.orElseGet(
          () -> new User("non-existing user", "no-email"));
    }
}

例は人為的ですが、一言で言えば、DAOパターンの背後にある動機を示しています。この場合、

main

メソッドは

UserDao

インスタンスを使用して、いくつかの

User

オブジェクトに対してCRUD操作を実行します。

  • このプロセスの最も重要な側面は、

    UserDao

    がオブジェクトの永続化、更新、および削除に関するすべての低レベルの詳細をアプリケーションから隠す方法です。


3 JPAでパターンを使用する

パターンはJPAのエンティティマネージャによって提供されるものの上に実装された抽象化および複雑さの単なる別の層になるので、JPAのリリースはDAOパターンの機能性をゼロにすると考える一般的な傾向があります。

確かに、いくつかのシナリオではこれは本当です。そうであっても


、エンティティマネージャのAPIのいくつかのドメイン固有のメソッドだけを私たちのアプリケーションに公開したいことがあります。


3.1.

JpaUserDao

クラス

それでは、

Dao

インターフェースの新しい実装を作成しましょう。そのため、JPAのエンティティマネージャが提供する機能をそのまま使用できるようにカプセル化できることがわかります。

public class JpaUserDao implements Dao<User> {

    private EntityManager entityManager;

   //standard constructors

    @Override
    public Optional<User> get(long id) {
        return Optional.ofNullable(entityManager.find(User.class, id));
    }

    @Override
    public List<User> getAll() {
        Query query = entityManager.createQuery("SELECT e FROM User e");
        return query.getResultList();
    }

    @Override
    public void save(User user) {
        executeInsideTransaction(entityManager -> entityManager.persist(user));
    }

    @Override
    public void update(User user, String[]params) {
        user.setName(Objects.requireNonNull(params[0], "Name cannot be null"));
        user.setEmail(Objects.requireNonNull(params[1], "Email cannot be null"));
        executeInsideTransaction(entityManager -> entityManager.merge(user));
    }

    @Override
    public void delete(User user) {
        executeInsideTransaction(entityManager -> entityManager.remove(user));
    }

    private void executeInsideTransaction(Consumer<EntityManager> action) {
        EntityTransaction tx = entityManager.getTransaction();
        try {
            tx.begin();
            action.accept(entityManager);
            tx.commit();
        }
        catch (RuntimeException e) {
            tx.rollback();
            throw e;
        }
    }
}


JpaUserDao

クラスは、JPA実装でサポートされている任意のリレーショナルデータベースと連携できます。

さらに、このクラスを詳しく見ると、https://en.wikipedia.org/wiki/Composition

over

inheritance[Composition]およびhttps://en.wikipedia.org/wiki/Dependency__injection[Dependencyの使用方法がわかります。インジェクション]を使用すると、アプリケーションで必要なエンティティマネージャメソッドのみを呼び出すことができます。

簡単に言うと、エンティティマネージャのAPI全体ではなく、ドメイン固有のカスタマイズAPIがあります。


3.2.

User

クラスをリファクタリングする

この場合は、JPAのデフォルト実装としてHibernateを使用します。したがって、

User

クラスを適宜リファクタリングします。

@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    private String name;
    private String email;

   //standard constructors/setters/getters
}


3.3. プログラムによるJPAエンティティマネージャのブートストラップ

MySQLの作業用インスタンスがローカルまたはリモートで実行されていて、データベーステーブル

“users”

にユーザーレコードが入力されていると仮定すると、JPAエンティティマネージャを取得する必要があるデータベース内。

ほとんどの場合、標準的なアプローチである典型的な

“ persistence.xml”

ファイルを介してこれを実現します。

この場合は、

“xml-less”

アプローチを取り、Hibernateの便利な


https://docs.jboss.org/hibernate/orm/5.0/javadocs/org/hibernate/を介して、プレーンJavaでエンティティマネージャを取得します。

jpa/boot/internal/EntityManagerFactoryBuilderImpl.html[EntityManagerFactoryBuilderImpl]

クラス。

JavaでJPA実装をブートストラップする方法の詳細な説明については、リンク/java-bootstrap-jpa[この記事]を確認してください。


3.4.

UserApplication

クラス

最後に、初期の

UserApplication

クラスをリファクタリングして、

JpaUserDao

インスタンスと連携し、

User

エンティティに対してCRUD操作を実行できるようにします。

public class UserApplication {

    private static Dao<User> jpaUserDao;

   //standard constructors

    public static void main(String[]args) {
        User user1 = getUser(1);
        System.out.println(user1);
        updateUser(user1, new String[]{"Jake", "[email protected]"});
        saveUser(new User("Monica", "[email protected]"));
        deleteUser(getUser(2));
        getAllUsers().forEach(user -> System.out.println(user.getName()));
    }

    public static User getUser(long id) {
        Optional<User> user = jpaUserDao.get(id);

        return user.orElseGet(
          () -> new User("non-existing user", "no-email"));
    }

    public static List<User> getAllUsers() {
        return jpaUserDao.getAll();
    }

    public static void updateUser(User user, String[]params) {
        jpaUserDao.update(user, params);
    }

    public static void saveUser(User user) {
        jpaUserDao.save(user);
    }

    public static void deleteUser(User user) {
        jpaUserDao.delete(user);
    }
}

例がかなり限られている場合でも、DAOパターンの機能をエンティティマネージャが提供する機能と統合する方法を説明するのに役立ちます。

ほとんどのアプリケーションには、

UpaApplication

クラスに

JpaUserDao

インスタンスを挿入する役割を担うDIフレームワークがあります。簡単にするために、このプロセスの詳細は省略しました。

ここで強調しなければならない最も重要な点は、

JpaUserDao

クラスがどのように

UserApplication

クラスを永続層がCRUD操作を実行するかについて完全に不可知論者であることを保つのを助けることです。

さらに、MySQLを他のRDBMSと(そしてフラットデータベースでさえも)入れ替えることができましたが、

Dao

インターフェースとエンティティマネージャによって提供される抽象化レベルのおかげで、アプリケーションは期待通りに機能し続けます。


4結論

この記事では、DAOパターンの重要な概念、それをJavaで実装する方法、およびJPAのエンティティー・マネージャーの上でそれを使用する方法について詳しく説明しました。

いつものように、この記事で示したすべてのコードサンプルはhttps://github.com/eugenp/tutorials/tree/master/patterns/design-patterns/src/main/java/com/baeldung/daopattern[over on GitHubから入手できます]。