1. 序章

このチュートリアルでは、JPAエンティティとJava Serializableインターフェースがどのようにブレンドされるかについて説明します。 まず、 java .io.Serializable インターフェースと、それが必要な理由を見ていきます。 その後、JPA仕様とその最も人気のある実装としてのHibernateを見ていきます。

2. シリアル化可能なインターフェイスとは何ですか?

Serializable は、コアJavaに見られる数少ないマーカーインターフェイスの1つです。 マーカーインターフェイスは、メソッドや定数のない特殊なケースのインターフェイスです。

オブジェクトのシリアル化は、Javaオブジェクトをバイトストリームに変換するプロセスです。 次に、これらのバイトストリームをネットワーク経由で転送したり、永続メモリに保存したりできます。逆シリアル化は逆のプロセスで、バイトストリームを取得してJavaオブジェクトに変換し直します。 オブジェクトのシリアル化(または逆シリアル化)を可能にするには、クラスはSerializableインターフェースを実装する必要があります。 それ以外の場合は、 java.io.NotSerializableExceptionが発生します。 シリアル化は、RMI、JPA、EJBなどのテクノロジーで広く使用されています。

3. JPAおよびSerializable

JPA仕様がSerializableについて何を述べているか、そしてそれがHibernateにどのように関係しているかを見てみましょう。

3.1. JPA仕様

JPAのコア部分の1つは、エンティティークラスです。 このようなクラスをエンティティとしてマークします( @Entity アノテーションまたはXML記述子のいずれかを使用)。 エンティティクラスが満たさなければならない要件がいくつかあります。JPA仕様によると、で最も懸念される要件は次のとおりです。

エンティティインスタンスがデタッチされたオブジェクトとして(たとえば、リモートインターフェイスを介して)値で渡される場合、entityクラスはSerializableを実装する必要があります]インターフェイス

実際には、オブジェクトがJVMのドメインを離れることである場合、シリアル化が必要になります。

各エンティティクラスは、永続的なフィールドとプロパティで構成されています。 この仕様では、エンティティのフィールドがJavaプリミティブ、Javaシリアル化可能タイプ、またはユーザー定義のシリアル化可能タイプである必要があります。

エンティティクラスにも主キーが必要です。 主キーは、プリミティブ(単一の永続フィールド)または複合にすることができます。 複合キーには複数のルールが適用されます。そのうちの1つは、複合キーをシリアル化可能にする必要があるというものです。

Hibernate、H2インメモリデータベース、およびUserIdを複合キーとして持つUserドメインオブジェクトを使用して簡単な例を作成しましょう。

@Entity
public class User {
    @EmbeddedId UserId userId;
    String email;
    
    // constructors, getters and setters
}

@Embeddable
public class UserId implements Serializable{
    private String name;
    private String lastName;
    
    // getters and setters
}

統合テストを使用して、ドメイン定義をテストできます。

@Test
public void givenUser_whenPersisted_thenOperationSuccessful() {
    UserId userId = new UserId();
    userId.setName("John");
    userId.setLastName("Doe");
    User user = new User(userId, "[email protected]");

    entityManager.persist(user);

    User userDb = entityManager.find(User.class, userId);
    assertEquals(userDb.email, "[email protected]");
}

UserIdクラスがSerializableインターフェースを実装していない場合、複合キーがインターフェースを実装する必要があるという具体的なメッセージとともにMappingExceptionを受け取ります。

3.2. Hibernate @JoinColumnアノテーション

Hibernateの公式ドキュメントでは、Hibernateでマッピングを説明する場合、@ JoinColumn アノテーションからreferencedColumnNameを使用する場合、参照フィールドはシリアル化可能である必要があることに注意してください。 通常、このフィールドは別のエンティティの主キーです。 複雑なエンティティクラスのまれなケースでは、参照をシリアル化可能にする必要があります。

以前のUserクラスを拡張してみましょう。ここで、emailフィールドはStringではなく、独立したエンティティです。 また、追加しますアカウントユーザーを参照し、フィールドを持つクラスタイプ。 ユーザー異なるタイプの複数のアカウントを持つことができます。 メールアドレスで検索する方が自然なので、アカウントメールでマッピングします。

@Entity
public class User {
    @EmbeddedId private UserId userId;
    private Email email;
}

@Entity
public class Email implements Serializable {
    @Id
    private long id;
    private String name;
    private String domain;
}

@Entity
public class Account {
    @Id
    private long id;
    private String type;
    @ManyToOne
    @JoinColumn(referencedColumnName = "email")
    private User user;
}

モデルをテストするために、ユーザー用に2つのアカウントを作成し、電子メールオブジェクトでクエリを実行するテストを作成します。

@Test
public void givenAssociation_whenPersisted_thenMultipleAccountsWillBeFoundByEmail() {
    // object creation 

    entityManager.persist(user);
    entityManager.persist(account);
    entityManager.persist(account2);

    List userAccounts = entityManager.createQuery("select a from Account a join fetch a.user where a.user.email = :email")
      .setParameter("email", email)
      .getResultList();
    
    assertEquals(userAccounts.size(), 2);
}

EmailクラスがSerializableインターフェースを実装していない場合、 MappingException が再び発生しますが、今回はやや不可解なメッセージが表示されます。 」。

3.3. プレゼンテーション層へのエンティティの公開

HTTPを使用してネットワーク経由でオブジェクトを送信する場合、通常、この目的のために特定のDTO(データ転送オブジェクト)を作成します。 DTOを作成することにより、内部ドメインオブジェクトを外部サービスから切り離します。 DTOを使用せずにエンティティをプレゼンテーション層に直接公開する場合は、エンティティをシリアル化可能にする必要があります

HttpSession オブジェクトを使用して、Webサイトへの複数のページアクセスにわたってユーザーを識別するのに役立つ関連データを保存します。 Webサーバーは、正常にシャットダウンするときにセッションデータをディスクに保存したり、クラスター環境の別のWebサーバーにセッションデータを転送したりできます。 エンティティがこのプロセスの一部である場合は、シリアル化可能である必要があります。 それ以外の場合は、NotSerializableExceptionが発生します。

4. 結論

この記事では、Javaシリアル化の基本について説明し、それがJPAでどのように機能するかを確認しました。 まず、Serializableに関するJPA仕様の要件を確認しました。 その後、JPAの最も人気のある実装としてHibernateを検討しました。 最後に、JPAエンティティがWebサーバーとどのように連携するかについて説明しました。

いつものように、この記事で紹介されているすべてのコードは、GitHubにあります。