Spring JPAにおける主キー生成戦略は、アプリケーションのパフォーマンス、スケーラビリティ、データベース設計に直接影響を与える重要な設計判断です。本記事では、@Id@GeneratedValueの基本から、GenerationTypeの各戦略(AUTO、IDENTITY、SEQUENCE、TABLE、UUID)の違い、シーケンス最適化(@SequenceGeneratorallocationSize)によるパフォーマンス向上、UUID主キーの採用基準、そして複合主キー(@EmbeddedId@IdClass)の実装パターンまで、主キー設計に関する実践的な知識を体系的に解説します。

実行環境と前提条件

本記事の内容を実践するにあたり、以下の環境を前提としています。

項目 バージョン・要件
Java 17以上
Spring Boot 3.4.x
Spring Data JPA 3.4.x(Spring Boot Starterに含まれる)
Hibernate 6.6.x(Spring Data JPAに含まれる)
Jakarta Persistence 3.2
データベース PostgreSQL 16 / MySQL 8.x / H2 Database
ビルドツール Maven または Gradle
IDE VS Code または IntelliJ IDEA

事前に以下の準備を完了してください。

  • JDK 17以上のインストール
  • Spring Boot + Spring Data JPAプロジェクトの基本構成
  • 永続化コンテキストの基本知識(永続化コンテキスト入門を参照)

@Idと@GeneratedValueの基本

JPAにおいて、エンティティの主キーは@Idアノテーションで定義します。主キーの値を自動生成する場合は、@GeneratedValueアノテーションを組み合わせて使用します。

@Idアノテーション

@Idアノテーションは、エンティティの識別子(主キー)となる属性を指定します。Jakarta Persistence仕様では、以下の型が主キー属性としてサポートされています。

  • Javaプリミティブ型(intlongなど)
  • プリミティブラッパー型(IntegerLongなど)
  • String
  • java.util.DateTemporalType.DATE
  • java.sql.Date
  • java.math.BigDecimal
  • java.math.BigInteger
  • UUID(Hibernate拡張)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Entity
public class Product {

    @Id
    private Long id;

    private String name;

    // getters and setters
}

上記の例では、id属性がエンティティの主キーとなります。@Idを付与した属性の位置(フィールドまたはgetter)によって、永続化コンテキストのアクセス戦略(フィールドアクセスまたはプロパティアクセス)が決定されます。

@GeneratedValueアノテーション

@GeneratedValueアノテーションは、主キー値の自動生成を有効にします。strategy属性で生成戦略を指定します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Entity
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

    private String name;

    // getters and setters
}

@GeneratedValueには以下の属性があります。

属性 説明 デフォルト値
strategy 主キー生成戦略を指定 GenerationType.AUTO
generator 使用するジェネレーター名を指定 ""

GenerationTypeの各戦略と選定基準

Jakarta Persistenceでは、5つの主キー生成戦略が定義されています。それぞれの特性を理解し、要件に応じた適切な戦略を選択することが重要です。

GenerationType.AUTO

AUTOは、永続化プロバイダ(Hibernate)にジェネレーターの選択を委ねる戦略です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Entity
public class Product {

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

    private String name;

    // getters and setters
}

Hibernate 6.xでは、AUTOは以下のロジックで生成戦略を決定します。

  1. 識別子の型がUUIDの場合、UUID生成を使用
  2. 識別子の型が数値型の場合、SequenceStyleGeneratorを使用(データベースがシーケンスをサポートしない場合はテーブル生成にフォールバック)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// UUIDの場合、自動的にUUID生成が選択される
@Entity
public class Order {

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

    private String orderNumber;

    // getters and setters
}

選定基準:

  • ポータビリティを重視する場合に有用
  • ただし、実際の動作がデータベースに依存するため、本番環境では明示的な戦略指定を推奨

GenerationType.IDENTITY

IDENTITYは、データベースのIDENTITY列(自動インクリメント)を使用する戦略です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Entity
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    // getters and setters
}

生成されるDDL(MySQLの場合):

1
2
3
4
5
CREATE TABLE product (
    id BIGINT NOT NULL AUTO_INCREMENT,
    name VARCHAR(255),
    PRIMARY KEY (id)
)

特性:

  • INSERTの実行後に識別子の値が確定する(post-insert generation)
  • JDBCバッチINSERTが無効化される(パフォーマンス上のデメリット)
  • シンプルで設定が容易

選定基準:

  • MySQLなどシーケンスをネイティブサポートしないデータベースで使用
  • バッチINSERTのパフォーマンスが重要でない場合

GenerationType.SEQUENCE

SEQUENCEは、データベースシーケンスを使用する戦略です。パフォーマンスと柔軟性の面で最も推奨される戦略です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Entity
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

    private String name;

    // getters and setters
}

明示的なシーケンス名を指定しない場合、Hibernateはテーブル名に基づいてシーケンス名を推測します(例: product_seq)。

生成されるDDL(PostgreSQLの場合):

1
2
3
4
5
6
7
CREATE SEQUENCE product_seq START WITH 1 INCREMENT BY 50;

CREATE TABLE product (
    id BIGINT NOT NULL,
    name VARCHAR(255),
    PRIMARY KEY (id)
)

特性:

  • INSERT前に識別子の値を取得可能(pre-insert generation)
  • JDBCバッチINSERTが有効
  • allocationSizeによる最適化が可能

選定基準:

  • PostgreSQL、Oracleなどシーケンスをサポートするデータベースでの第一選択
  • バッチ処理や高スループットが求められる場合

GenerationType.TABLE

TABLEは、専用のデータベーステーブルを使用して識別子を管理する戦略です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Entity
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.TABLE)
    private Long id;

    private String name;

    // getters and setters
}

生成されるDDL:

1
2
3
4
5
CREATE TABLE hibernate_sequences (
    sequence_name VARCHAR(255) NOT NULL,
    next_val BIGINT,
    PRIMARY KEY (sequence_name)
)

特性:

  • データベースに依存せずポータブル
  • テーブルロックが発生するため、パフォーマンスが低下する可能性
  • 別トランザクションでの値取得が必要

選定基準:

  • シーケンスもIDENTITYもサポートしないデータベースでの最終手段
  • 一般的には非推奨

GenerationType.UUID

UUIDは、UUID(Universally Unique Identifier)を使用する戦略です。Jakarta Persistence 3.1で追加されました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Entity
public class Product {

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

    private String name;

    // getters and setters
}

String型でも使用可能です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Entity
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private String id;

    private String name;

    // getters and setters
}

特性:

  • データベースに依存しない一意性
  • 分散システムでの主キー衝突を回避
  • インデックスの断片化やストレージサイズへの影響

選定基準:

  • マイクロサービスや分散システムでの使用
  • データベース間のデータ移行やレプリケーション

戦略比較表

戦略 バッチINSERT シーケンスサポート ポータビリティ パフォーマンス
AUTO 依存 依存
IDENTITY 不可 不要 低〜中
SEQUENCE 必要
TABLE 不要
UUID 不要 中〜高

シーケンス最適化とパフォーマンス

SEQUENCE戦略を使用する場合、@SequenceGeneratorアノテーションとallocationSizeパラメータを活用することで、データベースアクセスを大幅に削減できます。

@SequenceGeneratorの設定

@SequenceGeneratorを使用して、シーケンスの詳細な設定を行います。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
@Entity
public class Product {

    @Id
    @GeneratedValue(
        strategy = GenerationType.SEQUENCE,
        generator = "product_seq_generator"
    )
    @SequenceGenerator(
        name = "product_seq_generator",
        sequenceName = "product_sequence",
        initialValue = 1,
        allocationSize = 50
    )
    private Long id;

    private String name;

    // getters and setters
}

@SequenceGeneratorの主要な属性は以下の通りです。

属性 説明 デフォルト値
name ジェネレーター名(@GeneratedValuegenerator属性と一致させる) 必須
sequenceName データベースシーケンス名 プロバイダー依存
initialValue シーケンスの初期値 1
allocationSize シーケンスの増分値(最適化に重要) 50
schema シーケンスのスキーマ名 デフォルトスキーマ
catalog シーケンスのカタログ名 デフォルトカタログ

allocationSizeによる最適化

allocationSizeは、一度のシーケンス呼び出しで確保する識別子の範囲を指定します。

1
2
3
4
5
@SequenceGenerator(
    name = "product_seq_generator",
    sequenceName = "product_sequence",
    allocationSize = 50  // 50個の識別子をプールとして確保
)

この設定により、Hibernateは以下のように動作します。

  1. 最初のINSERT時にシーケンスから値を取得(例: 1)
  2. 1〜50までの識別子をメモリ内でプール
  3. 50個のエンティティをINSERTするまでシーケンスを再呼び出ししない
  4. プールを使い切ったら、再度シーケンスから値を取得(例: 51)
sequenceDiagram
    participant App as Application
    participant Hibernate as Hibernate
    participant DB as Database

    App->>Hibernate: persist(entity1)
    Hibernate->>DB: SELECT nextval('product_sequence')
    DB-->>Hibernate: 1
    Note over Hibernate: Pool: 1-50

    App->>Hibernate: persist(entity2)
    Note over Hibernate: Use from pool (2)
    
    App->>Hibernate: persist(entity3)
    Note over Hibernate: Use from pool (3)
    
    Note over Hibernate: ... 50個まではDB呼び出しなし ...
    
    App->>Hibernate: persist(entity51)
    Hibernate->>DB: SELECT nextval('product_sequence')
    DB-->>Hibernate: 51
    Note over Hibernate: Pool: 51-100

重要な注意点:

  • データベース側のシーケンス増分値(INCREMENT BY)はallocationSizeと一致させる必要があります
  • 不一致があると、Hibernate 6.xではSequenceMismatchStrategyの設定に従って処理されます
1
2
-- allocationSize = 50 の場合、データベースシーケンスも同様に設定
CREATE SEQUENCE product_sequence START WITH 1 INCREMENT BY 50;

Optimizerの種類

Hibernateは複数のOptimizerを提供しています。

Optimizer 説明
none 最適化なし。毎回DBにアクセス
pooled-lo シーケンス値をプールの下限として解釈(推奨)
pooled シーケンス値をプールの上限として解釈
hilo レガシー。非推奨

pooled-loがデフォルトで使用され、一般的に最も適切な選択です。

UUID主キーのメリット・デメリットと採用基準

UUID(Universally Unique Identifier)を主キーとして使用するケースが増えています。特に分散システムやマイクロサービスアーキテクチャでは重要な選択肢です。

UUID主キーの実装

Hibernate 6.xでは、@UuidGeneratorアノテーションを使用して詳細な設定が可能です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import org.hibernate.annotations.UuidGenerator;

@Entity
public class Order {

    @Id
    @GeneratedValue
    @UuidGenerator(style = UuidGenerator.Style.RANDOM)
    private UUID id;

    private String orderNumber;
    
    private LocalDateTime orderDate;

    // getters and setters
}

@UuidGeneratorstyle属性では以下の生成方式を選択できます。

Style 説明
RANDOM ランダムなUUID(バージョン4)を生成(デフォルト)
TIME 時間ベースのUUID(バージョン1、バリアント2)を生成
AUTO プロバイダーに選択を委ねる
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// 時間ベースのUUID生成
@Entity
public class Event {

    @Id
    @GeneratedValue
    @UuidGenerator(style = UuidGenerator.Style.TIME)
    private UUID id;

    private String eventName;

    // getters and setters
}

UUID主キーのメリット

1. 分散システムでの一意性保証

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// 複数のサービスインスタンスで同時にエンティティを作成しても衝突しない
@Service
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;

    @Transactional
    public Order createOrder(OrderRequest request) {
        Order order = new Order();
        // UUIDは永続化前にアプリケーション側で生成可能
        order.setOrderNumber(request.getOrderNumber());
        return orderRepository.save(order);
    }
}

2. 永続化前の識別子取得

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@Entity
public class Document {

    @Id
    @GeneratedValue
    @UuidGenerator
    private UUID id;

    // 永続化前でも識別子を使用可能
    public String generateFileName() {
        return id.toString() + ".pdf";
    }
}

3. データベース間のマージ・レプリケーション

異なるデータベースインスタンスで作成されたデータを統合する際、主キーの衝突を心配する必要がありません。

UUID主キーのデメリット

1. ストレージサイズ

サイズ
BIGINT 8バイト
UUID (バイナリ) 16バイト
UUID (文字列) 36バイト

2. インデックスの断片化

ランダムなUUIDは順序性がないため、B-treeインデックスの断片化を引き起こします。

1
2
// 時間ベースのUUIDを使用することで断片化を軽減
@UuidGenerator(style = UuidGenerator.Style.TIME)

3. クエリの可読性低下

1
2
3
4
5
-- 数値ID
SELECT * FROM orders WHERE id = 12345;

-- UUID
SELECT * FROM orders WHERE id = '550e8400-e29b-41d4-a716-446655440000';

UUID採用の判断基準

UUIDを採用すべきケース:

  • マイクロサービス間でのデータ連携
  • オフライン対応アプリケーション(クライアント側でID生成)
  • データベースシャーディング環境
  • 外部公開APIでの識別子(推測困難)

シーケンスを採用すべきケース:

  • 単一データベースの従来型アプリケーション
  • 高頻度のレンジスキャンクエリ
  • ストレージコストが重要な大規模データ
  • 監査やデバッグで順序が重要な場合

複合主キーの実装パターン

複合主キー(Composite Primary Key)は、複数の属性の組み合わせでエンティティを一意に識別する場合に使用します。JPAでは@EmbeddedId@IdClassの2つのアプローチを提供しています。

複合主キー実装の共通要件

Jakarta Persistence仕様では、複合主キークラスに以下の要件を定めています。

  • publicクラスで、引数なしのpublicコンストラクタを持つ
  • Serializableインターフェースを実装
  • equals()hashCode()メソッドを適切に実装

@EmbeddedIdによる実装

@EmbeddedIdは、複合主キーを@Embeddableクラスとして定義し、エンティティに埋め込む方式です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import java.io.Serializable;
import java.util.Objects;
import jakarta.persistence.Embeddable;

@Embeddable
public class OrderItemId implements Serializable {

    private Long orderId;
    
    private Long productId;

    // 引数なしコンストラクタ(必須)
    public OrderItemId() {
    }

    public OrderItemId(Long orderId, Long productId) {
        this.orderId = orderId;
        this.productId = productId;
    }

    // getters and setters

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        OrderItemId that = (OrderItemId) o;
        return Objects.equals(orderId, that.orderId) 
            && Objects.equals(productId, that.productId);
    }

    @Override
    public int hashCode() {
        return Objects.hash(orderId, productId);
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Entity;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.MapsId;

@Entity
public class OrderItem {

    @EmbeddedId
    private OrderItemId id;

    @ManyToOne
    @MapsId("orderId")  // EmbeddedIdの属性名を指定
    private Order order;

    @ManyToOne
    @MapsId("productId")
    private Product product;

    private Integer quantity;
    
    private BigDecimal unitPrice;

    // getters and setters
}

@EmbeddedIdの特徴:

  • 主キークラスが独立したオブジェクトとして扱える
  • 主キーの参照が明確(entity.getId().getOrderId()
  • @MapsIdを使用して関連エンティティと主キー属性を紐付け可能

@IdClassによる実装

@IdClassは、エンティティに個別の@Id属性を定義し、別のクラスで主キーの構造を指定する方式です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import java.io.Serializable;
import java.util.Objects;

public class OrderItemId implements Serializable {

    private Long orderId;
    
    private Long productId;

    public OrderItemId() {
    }

    public OrderItemId(Long orderId, Long productId) {
        this.orderId = orderId;
        this.productId = productId;
    }

    // getters and setters

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        OrderItemId that = (OrderItemId) o;
        return Objects.equals(orderId, that.orderId) 
            && Objects.equals(productId, that.productId);
    }

    @Override
    public int hashCode() {
        return Objects.hash(orderId, productId);
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.IdClass;
import jakarta.persistence.ManyToOne;

@Entity
@IdClass(OrderItemId.class)
public class OrderItem {

    @Id
    @ManyToOne
    private Order order;

    @Id
    @ManyToOne
    private Product product;

    private Integer quantity;
    
    private BigDecimal unitPrice;

    // getters and setters
}

@IdClassの特徴:

  • エンティティの属性に直接アクセス可能(entity.getOrderId()
  • 主キークラスに@Embeddableが不要
  • レガシーテーブルとのマッピングに柔軟

@EmbeddedIdと@IdClassの比較

観点 @EmbeddedId @IdClass
主キーへのアクセス entity.getId().getX() entity.getX()
主キークラスの要件 @Embeddable必須 @Embeddable不要
JPQLクエリ WHERE e.id.x = :x WHERE e.x = :x
オブジェクト指向性 高い 低い
Spring Data JPAサポート 完全 完全

複合主キーでのリポジトリ定義

Spring Data JPAでは、複合主キーを持つエンティティのリポジトリを以下のように定義します。

1
2
3
4
5
6
7
8
9
import org.springframework.data.jpa.repository.JpaRepository;

// @EmbeddedIdの場合
public interface OrderItemRepository extends JpaRepository<OrderItem, OrderItemId> {
    
    List<OrderItem> findByIdOrderId(Long orderId);
    
    List<OrderItem> findByIdProductId(Long productId);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// 使用例
@Service
public class OrderItemService {

    @Autowired
    private OrderItemRepository repository;

    public OrderItem findOrderItem(Long orderId, Long productId) {
        OrderItemId id = new OrderItemId(orderId, productId);
        return repository.findById(id)
            .orElseThrow(() -> new EntityNotFoundException("OrderItem not found"));
    }
}

よくある誤解とアンチパターン

アンチパターン1: IDENTITYとバッチ処理の併用

IDENTITY戦略ではJDBCバッチINSERTが無効化されるため、大量データの一括挿入でパフォーマンスが大幅に低下します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// アンチパターン: IDENTITYとバッチ処理
@Entity
public class LogEntry {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)  // バッチ無効
    private Long id;
    
    // ...
}

// 改善: SEQUENCEを使用
@Entity
public class LogEntry {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    @SequenceGenerator(name = "log_seq", allocationSize = 100)
    private Long id;
    
    // ...
}

アンチパターン2: allocationSizeとシーケンス増分の不一致

allocationSizeとデータベースシーケンスのINCREMENT BYが一致しないと、主キーの重複や欠番が発生します。

1
2
3
4
5
6
7
// アンチパターン: 不一致
@SequenceGenerator(
    name = "product_seq",
    sequenceName = "product_sequence",
    allocationSize = 50  // Javaでは50
)
// データベース側: INCREMENT BY 1  // DBでは1 → 不一致!
1
2
3
4
5
6
7
// 正しい設定
@SequenceGenerator(
    name = "product_seq",
    sequenceName = "product_sequence",
    allocationSize = 50
)
// データベース側: INCREMENT BY 50  // 一致

アンチパターン3: 複合主キーでのequals/hashCode未実装

複合主キークラスでequals()hashCode()を適切に実装しないと、永続化コンテキストでのエンティティ同一性が壊れます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// アンチパターン: equals/hashCode未実装
@Embeddable
public class OrderItemId implements Serializable {
    private Long orderId;
    private Long productId;
    // equals/hashCodeなし → Set/Mapで正しく動作しない
}

// 正しい実装
@Embeddable
public class OrderItemId implements Serializable {
    private Long orderId;
    private Long productId;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        OrderItemId that = (OrderItemId) o;
        return Objects.equals(orderId, that.orderId) 
            && Objects.equals(productId, that.productId);
    }

    @Override
    public int hashCode() {
        return Objects.hash(orderId, productId);
    }
}

アンチパターン4: プリミティブ型での主キー定義

プリミティブ型(longint)を主キーに使用すると、新規エンティティと永続化済みエンティティの区別が困難になります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// アンチパターン: プリミティブ型
@Entity
public class Product {

    @Id
    @GeneratedValue
    private long id;  // デフォルト値が0 → 新規か永続化済みか判別困難
}

// 推奨: ラッパー型
@Entity
public class Product {

    @Id
    @GeneratedValue
    private Long id;  // nullなら新規エンティティと判別可能
}

まとめと実践Tips

主キー生成戦略の選定フローチャート

flowchart TD
    A[主キー生成戦略の選定] --> B{分散システム?}
    B -->|Yes| C{クライアント側ID生成?}
    B -->|No| D{DBがシーケンスサポート?}
    
    C -->|Yes| E[UUID]
    C -->|No| F{DBがシーケンスサポート?}
    
    F -->|Yes| G[SEQUENCE]
    F -->|No| H[UUID or TABLE]
    
    D -->|Yes| I[SEQUENCE 推奨]
    D -->|No| J{バッチ処理重視?}
    
    J -->|Yes| K[TABLE]
    J -->|No| L[IDENTITY]
    
    I --> M[allocationSizeで最適化]
    G --> M
~~~

### 実践Tips

**1. シーケンス戦略での最適化設定**

~~~yaml
# application.yml
spring:
  jpa:
    properties:
      hibernate:
        jdbc:
          batch_size: 50
        order_inserts: true
        order_updates: true
~~~

**2. UUID使用時のストレージ最適化**

~~~java
// バイナリ形式で保存(16バイト)
@Entity
public class Document {

    @Id
    @GeneratedValue
    @JdbcTypeCode(Types.BINARY)
    private UUID id;
}
~~~

**3. 開発環境でのシーケンス確認**

~~~java
// シーケンス不一致時の動作設定
spring.jpa.properties.hibernate.id.sequence.increment_size_mismatch_strategy=LOG
~~~

**4. テスト時の主キー設定**

~~~java
@DataJpaTest
class ProductRepositoryTest {

    @Autowired
    private TestEntityManager entityManager;

    @Test
    void testSaveProduct() {
        Product product = new Product();
        product.setName("Test Product");
        
        Product saved = entityManager.persistAndFlush(product);
        
        assertThat(saved.getId()).isNotNull();
    }
}
~~~

主キー生成戦略の選択は、アプリケーションのライフサイクル全体に影響を与える重要な設計判断です。本記事で解説した各戦略の特性と選定基準を参考に、要件に最適な戦略を選択してください。

## 参考リンク

- [Hibernate ORM 6.6 User Guide - Identifiers](https://docs.hibernate.org/orm/6.6/userguide/html_single/Hibernate_User_Guide.html#identifiers)
- [Spring Data JPA Reference - Entity Persistence](https://docs.spring.io/spring-data/jpa/reference/jpa/entity-persistence.html)
- [Jakarta Persistence 3.2 Specification](https://jakarta.ee/specifications/persistence/3.2/)
- [Hibernate GitHub Repository](https://github.com/hibernate/hibernate-orm)