JPAはいつ主キーを設定しますか
1. 概要
このチュートリアルでは、JPAが主キーに値を割り当てる瞬間を説明します。 JPA仕様の内容を明確にしてから、主キー生成にさまざまなJPA戦略を使用した例を示します。
2. 問題文
ご存知のとおり、JPA(Java Persistence API)は、 EntityManager を使用して、Entityのライフサイクルを管理します。 ある時点で、JPAプロバイダーは主キーに値を割り当てる必要があります。 それで、これはいつ起こるのでしょうか? そして、これを述べているドキュメントはどこにありますか?
JPA仕様には次のように書かれています。
新しいエンティティインスタンスは、persistメソッドを呼び出すか、persist操作をカスケードすることにより、管理対象と永続化の両方になります。
したがって、この記事では EntityManager.persist()メソッドに焦点を当てます。
3. 価値の生成戦略
EntityManager.persist()メソッドを呼び出すと、エンティティの状態はJPA仕様に従って変更されます。
Xが新しいエンティティの場合、管理対象になります。 エンティティXは、トランザクションのコミット時またはその前、またはフラッシュ操作の結果としてデータベースに入力されます。
これは、主キーを生成するさまざまな方法があることを意味します。 一般に、2つの解決策があります。
- 主キーを事前に割り当てます
- データベースに永続化した後、主キーを割り当てます
- GenerationType.AUTO
- GenerationType.IDENTITY
- GenerationType.SEQUENCE
- GenerationType.TABLE
3.1. GenerationType.AUTO
AUTO は、@GeneratedValueのデフォルト戦略です。 主キーが必要な場合は、AUTO戦略を使用できます。 JPAプロバイダーは、基盤となるデータベースに適切な戦略を選択します。
@Entity
@Table(name = "app_admin")
public class Admin {
@Id
@GeneratedValue
private Long id;
@Column(name = "admin_name")
private String name;
// standard getters and setters
}
3.2. GenerationType.IDENTITY
IDENTITY戦略は、データベースの自動インクリメント列に依存しています。 データベースは、各挿入操作の後に主キーを生成します。 JPAは、挿入操作の実行後またはトランザクションのコミット時に主キー値を割り当てます。
@Entity
@Table(name = "app_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "user_name")
private String name;
// standard getters and setters
}
ここでは、トランザクションコミットの前後のid値を確認します。
@Test
public void givenIdentityStrategy_whenCommitTransction_thenReturnPrimaryKey() {
User user = new User();
user.setName("TestName");
entityManager.getTransaction().begin();
entityManager.persist(user);
Assert.assertNull(user.getId());
entityManager.getTransaction().commit();
Long expectPrimaryKey = 1L;
Assert.assertEquals(expectPrimaryKey, user.getId());
}
IDENTITY 戦略は、MySQL、SQL Server、PostgreSQL、DB2、Derby、およびSybaseでサポートされています。
3.3. GenerationType.SEQUENCE
SEQUENCE 戦略を使用することにより、JPAはデータベースシーケンスを使用して主キーを生成します。 この戦略を適用する前に、まずデータベース側でシーケンスを作成する必要があります。
CREATE SEQUENCE article_seq
MINVALUE 1
START WITH 50
INCREMENT BY 50
JPAは、EntityManager.persist()メソッドを呼び出した後、トランザクションをコミットする前に、主キーを設定します。
SEQUENCE戦略でArticleエンティティを定義しましょう。
@Entity
@Table(name = "article")
public class Article {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "article_gen")
@SequenceGenerator(name="article_gen", sequenceName="article_seq")
private Long id;
@Column(name = "article_name")
private String name
// standard getters and setters
}
シーケンスは50から始まるため、最初のidは次の値51になります。
それでは、SEQUENCE戦略をテストしてみましょう。
@Test
public void givenSequenceStrategy_whenPersist_thenReturnPrimaryKey() {
Article article = new Article();
article.setName("Test Name");
entityManager.getTransaction().begin();
entityManager.persist(article);
Long expectPrimaryKey = 51L;
Assert.assertEquals(expectPrimaryKey, article.getId());
entityManager.getTransaction().commit();
}
SEQUENCE 戦略は、Oracle、PostgreSQL、およびDB2でサポートされています。
3.4. GenerationType.TABLE
TABLE 戦略は、テーブルから主キーを生成し、基盤となるデータベースに関係なく同じように機能します。
主キーを生成するには、データベース側でジェネレータテーブルを作成する必要があります。 テーブルには少なくとも2つの列が必要です。1つはジェネレーターの名前を表す列で、もう1つは主キー値を格納する列です。
まず、ジェネレータテーブルを作成しましょう。
@Table(name = "id_gen")
@Entity
public class IdGenerator {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "gen_name")
private String gen_name;
@Column(name = "gen_value")
private Long gen_value;
// standard getters and setters
}
次に、ジェネレータテーブルに2つの初期値を挿入する必要があります。
INSERT INTO id_gen (gen_name, gen_val) VALUES ('id_generator', 0);
INSERT INTO id_gen (gen_name, gen_val) VALUES ('task_gen', 10000);
JPAは、 EntityManager.persist()メソッドを呼び出した後、トランザクションをコミットする前に主キー値を割り当てます。
次に、TABLE戦略でジェネレーターテーブルを使用してみましょう。 alllocationSize を使用して、いくつかの主キーを事前に割り当てることができます。
@Entity
@Table(name = "task")
public class Task {
@TableGenerator(name = "id_generator", table = "id_gen", pkColumnName = "gen_name", valueColumnName = "gen_value",
pkColumnValue="task_gen", initialValue=10000, allocationSize=10)
@Id
@GeneratedValue(strategy = GenerationType.TABLE, generator = "id_generator")
private Long id;
@Column(name = "name")
private String name;
// standard getters and setters
}
そして、 persist メソッドを呼び出した後、idは10,000から始まります。
@Test
public void givenTableStrategy_whenPersist_thenReturnPrimaryKey() {
Task task = new Task();
task.setName("Test Task");
entityManager.getTransaction().begin();
entityManager.persist(task);
Long expectPrimaryKey = 10000L;
Assert.assertEquals(expectPrimaryKey, task.getId());
entityManager.getTransaction().commit();
}
4. 結論
この記事では、JPAがさまざまな戦略の下で主キーを設定する瞬間を説明します。 さらに、例を通じてこれらの各戦略の使用法についても学びました。
完全なコードは、GitHubのにあります。