JPAとHibernateを使用したJavaPersistence、パート1:エンティティと関係

Java Persistence API(JPA)は、リレーショナルデータベースとオブジェクト指向プログラミングの間のギャップを埋めるJava仕様です。この2部構成のチュートリアルでは、JPAを紹介し、JavaオブジェクトをJPAエンティティとしてモデル化する方法、エンティティの関係を定義する方法、およびEntityManagerJavaアプリケーションのリポジトリパターンでJPAを使用する方法について説明します。

このチュートリアルでは、JPAプロバイダーとしてHibernateを使用していることに注意してください。ほとんどの概念は、他のJava永続性フレームワークに拡張できます。

JPAとは何ですか?

JPAと、EJB 3.0を含む関連フレームワークの進化については、「JPAとは?Java PersistenceAPIの概要」を参照してください。およびJDBC。

JPAのオブジェクト関係

リレーショナルデータベースは、1970年代からプログラムデータを保存する手段として存在してきました。今日の開発者はリレーショナルデータベースに代わる多くの選択肢を持っていますが、このタイプのデータベースはスケーラブルでよく理解されており、小規模および大規模なソフトウェア開発で今でも広く使用されています。

リレーショナルデータベースコンテキストのJavaオブジェクトは、エンティティとして定義されます。エンティティは、列と行を占めるテーブルに配置されます。プログラマーは、外部キー結合テーブルを使用して、エンティティ間の関係(つまり、1対1、1対多、および多対多の関係)を定義します。SQL(Structured Query Language)を使用して、外部キー制約を使用して、個々のテーブル内および複数のテーブル間でデータを取得して操作することもできます。リレーショナルモデルはフラットですが、開発者はクエリを記述してデータを取得し、そのデータからオブジェクトを構築できます。

オブジェクト関係インピーダンスの不一致

データオブジェクトをリレーショナルデータベースにマッピングするという課題を指す、オブジェクト関係インピーダンスミスマッチという用語に精通しているかもしれません。この不一致は、オブジェクト指向設計が1対1、1対多、および多対多の関係に限定されないために発生します。代わりに、オブジェクト指向設計では、オブジェクト、その属性と動作、およびオブジェクトの関係について考えます。2つの例は、カプセル化と継承です。

  • オブジェクトに別のオブジェクトが含まれている場合は、カプセル化--ahas -a関係を介してこれを定義します。
  • オブジェクトが別のオブジェクトの特殊化である場合、継承is-a関係)によってこれを定義します。

関連付け、集約、構成、抽象化、一般化、実現、および依存関係はすべて、オブジェクト指向プログラミングの概念であり、リレーショナルモデルへのマッピングが難しい場合があります。

ORM:オブジェクトリレーショナルマッピング

オブジェクト指向設計とリレーショナルデータベースモデリングの不一致により、オブジェクトリレーショナルマッピング(ORM)専用に開発されたツールのクラスが生まれました。Hibernate、EclipseLink、iBatisなどのORMツールは、エンティティとその関係を含むリレーショナルデータベースモデルをオブジェクト指向モデルに変換します。これらのツールの多くはJPA仕様の前に存在していましたが、標準がなければ、それらの機能はベンダーに依存していました。

2006年にEJB3.0の一部として最初にリリースされたJavaPersistence API(JPA)は、オブジェクトに注釈を付けて、リレーショナルデータベースにマッピングおよび格納できるようにする標準的な方法を提供します。この仕様では、データベースと対話するための一般的な構成も定義されています。JavaのORM標準があると、ベンダーの実装に一貫性がもたらされると同時に、柔軟性とアドオンも可能になります。例として、元のJPA仕様はリレーショナルデータベースに適用できますが、一部のベンダー実装では、NoSQLデータベースで使用するためにJPAが拡張されています。

JPAの進化

JPAの最初のリリースであるバージョン1.0は、Java Community Process(JCP)を通じてJava Specification Request(JSR)220として2006年に公開されました。バージョン2.0(JSR 317)は2009年に公開され、バージョン2.1(JSR 338)は2013年に公開されました。バージョン2.2(JSR 338のメンテナンスリリース)は2017年に公開されました。JPA2.2は、JakartaEEに含まれ進行中の開発のために選択されました。

JPA入門

Java Persistence APIは仕様であり、実装ではありません。コードでORM製品と対話するために使用できる一般的な抽象化を定義します。このセクションでは、JPA仕様の重要な部分のいくつかを確認します。

次の方法を学習します。

  • データベース内のエンティティ、フィールド、および主キーを定義します。
  • データベース内のエンティティ間の関係を作成します。
  • 協力EntityManagerし、そのメソッド。

エンティティの定義

エンティティを定義するには、@Entityアノテーションが付けられたクラスを作成する必要があります。@Entity注釈は、あるマーカー注釈永続エンティティを発見するために使用され、。たとえば、本のエンティティを作成する場合は、次のように注釈を付けます。

 @Entity public class Book { ... } 

デフォルトでは、このエンティティはBook、指定されたクラス名によって決定されるように、テーブルにマップされます。このエンティティを別のテーブル(およびオプションで特定のスキーマ)にマップする場合は、@Tableアノテーションを使用してそれを行うことができます。BookクラスをBOOKSテーブルにマップする方法は次のとおりです。

 @Entity @Table(name="BOOKS") public class Book { ... } 

BOOKSテーブルがPUBLISHINGスキーマにある場合は、スキーマを@Table注釈に追加できます。

 @Table(name="BOOKS", schema="PUBLISHING") 

フィールドを列にマッピングする

エンティティがテーブルにマップされたら、次のタスクはそのフィールドを定義することです。フィールドはクラスのメンバー変数として定義され、各フィールドの名前はテーブルの列名にマップされます。次に@Column示すように、注釈を使用してこのデフォルトのマッピングをオーバーライドできます。

 @Entity @Table(name="BOOKS") public class Book { private String name; @Column(name="ISBN_NUMBER") private String isbn; ... } 

この例では、name属性のデフォルトマッピングを受け入れましたが、属性のカスタムマッピングを指定しましたisbnname属性はにマッピングされる名前の列が、isbn属性がISBN_NUMBER列にマップされます。

The @Column annotation allows us to define additional properties of the field/column, including length, whether it is nullable, whether it must be unique, its precision and scale (if it's a decimal value), whether it is insertable and updatable, and so forth.

Specifying the primary key

One of the requirements for a relational database table is that it must contain a primary key, or a key that uniquely identifies a specific row in the database. In JPA, we use the @Id annotation to designate a field to be the table's primary key. The primary key is required to be a Java primitive type, a primitive wrapper, such as Integer or Long, a String, a Date, a BigInteger, or a BigDecimal.

In this example, we map the id attribute, which is an Integer, to the ID column in the BOOKS table:

 @Entity @Table(name="BOOKS") public class Book { @Id private Integer id; private String name; @Column(name="ISBN_NUMBER") private String isbn; ... } 

It is also possible to combine the @Id annotation with the @Column annotation to overwrite the primary key's column-name mapping.

Relationships between entities

Now that you know how to define an entity, let's look at how to create relationships between entities. JPA defines four annotations for defining entities:

  • @OneToOne
  • @OneToMany
  • @ManyToOne
  • @ManyToMany

One-to-one relationships

The @OneToOne annotation is used to define a one-to-one relationship between two entities. For example, you may have a User entity that contains a user's name, email, and password, but you may want to maintain additional information about a user (such as age, gender, and favorite color) in a separate UserProfile entity. The @OneToOne annotation facilitates breaking down your data and entities this way.

The User class below has a single UserProfile instance. The UserProfile maps to a single User instance.

 @Entity public class User { @Id private Integer id; private String email; private String name; private String password; @OneToOne(mappedBy="user") private UserProfile profile; ... } 
 @Entity public class UserProfile { @Id private Integer id; private int age; private String gender; private String favoriteColor; @OneToOne private User user; ... } 

The JPA provider uses UserProfile's user field to map UserProfile to User. The mapping is specified in the mappedBy attribute in the @OneToOne annotation.

One-to-many and many-to-one relationships

The @OneToMany and @ManyToOne annotations facilitate both sides of the same relationship. Consider an example where a Book can have only one Author, but an Author may have many books. The Book entity would define a @ManyToOne relationship with Author and the Author entity would define a @OneToMany relationship with Book.

 @Entity public class Book { @Id private Integer id; private String name; @ManyToOne @JoinColumn(name="AUTHOR_ID") private Author author; ... } 
 @Entity public class Author { @Id @GeneratedValue private Integer id; private String name; @OneToMany(mappedBy = "author") private List books = new ArrayList(); ... } 

In this case, the Author class maintains a list of all of the books written by that author and the Book class maintains a reference to its single author. Additionally, the @JoinColumn specifies the name of the column in the Book table to store the ID of the Author.

Many-to-many relationships

Finally, the @ManyToMany annotation facilitates a many-to-many relationship between entities. Here's a case where a Book entity has multiple Authors:

 @Entity public class Book { @Id private Integer id; private String name; @ManyToMany @JoinTable(name="BOOK_AUTHORS", [email protected](name="BOOK_ID"), [email protected](name="AUTHOR_ID")) private Set authors = new HashSet(); ... } 
 @Entity public class Author { @Id @GeneratedValue private Integer id; private String name; @ManyToMany(mappedBy = "author") private Set books = new HashSet(); ... } 

In this example, we create a new table, BOOK_AUTHORS, with two columns: BOOK_ID and AUTHOR_ID. Using the joinColumns and inverseJoinColumns attributes tells your JPA framework how to map these classes in a many-to-many relationship. The @ManyToMany annotation in the Author class references the field in the Book class that manages the relationship; namely the authors property.

That's a quick demo for a fairly complex topic. We'll dive further into the @JoinTable and @JoinColumn annotations in the next article.

Working with the EntityManager

EntityManager is the class that performs database interactions in JPA. It is initialized through a configuration file named persistence.xml. This file is found in the META-INF folder in your CLASSPATH, which is typically packaged in your JAR or WAR file. The persistence.xml file contains:

  • The named "persistence unit," which specifies the persistence framework you're using, such as Hibernate or EclipseLink.
  • A collection of properties specifying how to connect to your database, as well as any customizations in the persistence framework.
  • A list of entity classes in your project.

Let's look at an example.

Configuring the EntityManager

First, we create an EntityManager using the EntityManagerFactory retrieved from the Persistence class:

 EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("Books"); EntityManager entityManager = entityManagerFactory.createEntityManager(); 

In this case we've created an EntityManager that is connected to the "Books" persistence unit, which we've configured in the persistence.xml file.

The EntityManager class defines how our software will interact with the database through JPA entities. Here are some of the methods used by EntityManager:

  • find retrieves an entity by its primary key.
  • createQuery creates a Query instance that can be used to retrieve entities from the database.
  • createNamedQuery loads a Query that has been defined in a @NamedQuery annotation inside one of the persistence entities. Named queries provide a clean mechanism for centralizing JPA queries in the definition of the persistence class on which the query will execute.
  • getTransaction defines an EntityTransaction to use in your database interactions. Just like database transactions, you will typically begin the transaction, perform your operations, and then either commit or rollback your transaction. The getTransaction() method lets you access this behavior at the level of the EntityManager, rather than the database.
  • merge() adds an entity to the persistence context, so that when the transaction is committed, the entity will be persisted to the database. When using merge(), objects are not managed.
  • persist adds an entity to the persistence context, so that when the transaction is committed, the entity will be persisted to the database. When using persist(), objects are managed.
  • refresh refreshes the state of the current entity from the database.
  • flush synchronizes the state of the persistence context with the database.

これらすべてのメソッドを一度に統合することを心配する必要はありません。を直接操作することでEntityManager、それらを知ることができます。これについては、次のセクションで詳しく説明します。