1.

Hibernate ids must be manually assigned エラー対処

▶
この記事の要点
  • IdentifierGenerationException: ids for this class must be manually assigned before calling save() は「ID を手動で入れる設定なのに ID が空のまま保存しようとした」エラー
  • hbm.xml で <generator> を書かない、または class="assigned" にすると「手動代入」扱いになる
  • JPA アノテーションでは @Id だけ付けて @GeneratedValue がないと同じ状態
  • 対処 1: 保存前にエンティティへ ID を代入する(自然キー・UUID を自前で採番する場合)
  • 対処 2: DB の自動採番に任せるなら <generator class="native"/> や @GeneratedValue(strategy = GenerationType.IDENTITY) を指定する
  • Hibernate 6 系では文言が変わり「must be manually assigned before calling 'persist()'」の形で出る

エラー内容

net.sf.hibernate.id.IdentifierGenerationException: ids for this class must be manually assigned before calling save()

net.sf.hibernate は Hibernate 2 系のパッケージ名です。Hibernate 3〜5 系では org.hibernate.id.IdentifierGenerationException となり、メッセージの末尾にエンティティのクラス名が付きます。Hibernate 6 系では文言が「Identifier of entity '...' must be manually assigned before calling 'persist()'」のように変わっていますが、原因と対処は同じです。

発生条件と原因

Hibernate のマッピングで主キー(id)の生成方法が「アプリ側で手動代入(assigned)」になっているのに、エンティティ(Bean)の id に値を入れずに save() / persist() を呼ぶと発生します。DB 側でいう PK に当たる項目に値がない状態で INSERT しようとしているため、Hibernate が事前に止めています。

「手動代入」扱いになるのは次のようなケースです。

  • hbm.xml の <id> 内に <generator> を書いていない(省略時は assigned になる)
  • <generator class="assigned"/> を明示している
  • JPA アノテーションで @Id は付けたが @GeneratedValue を付けていない

DB のテーブルに AUTO_INCREMENT を設定していても、Hibernate のマッピング側でそれを伝えていなければ Hibernate は「アプリが ID を入れるはず」と判断します。DB の設定とマッピングの設定は別物という点が、このエラーの一番のつまずきどころです。

対処法

対処 1: 保存前に ID を代入する

社員番号や商品コードのような「業務上意味のあるキー(自然キー)」や、アプリ側で UUID を採番する設計なら、id を手動代入のままにして、保存前に必ず値をセットします。

User user = new User();
user.setId("U0001");          // 手動で ID を代入してから
user.setName("山田");
session.save(user);           // 保存する

対処 2: 自動採番に切り替える(hbm.xml)

DB 側で AUTO_INCREMENT(MySQL)やシーケンス(Oracle / PostgreSQL)を使っている場合は、id タグ内に generator を指定します。

<id name="id" column="ID" type="long">
    <generator class="native"/>
</id>
generator動き向いている DB
assignedアプリが ID を代入(省略時の既定)自然キー・自前 UUID
identityDB の自動採番列を使うMySQL / MariaDB / SQL Server
sequenceDB のシーケンスから取得Oracle / PostgreSQL
nativeDB の方言に合わせて方式を自動選択DB を切り替える可能性がある場合

対処 2': 自動採番に切り替える(JPA アノテーション)

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // MySQL の AUTO_INCREMENT を使う
    private Long id;

    private String name;
}

落とし穴

  • DB 側に自動採番がない: IDENTITY を指定しても、テーブルの列に AUTO_INCREMENT が付いていなければ INSERT 時に別のエラー(ID が NULL / 既定値なし)になる。マッピングと DDL をそろえる
  • ID の型をプリミティブにしている: int id だと未設定でも 0 が入るため、このエラーは出ずに「重複キー」エラーや既存行の上書きとして表面化することがある。ID はラッパー型(Long / Integer)にすると未設定を null で判別できる
  • GenerationType.AUTO の挙動: Hibernate 5 以降を MySQL で使うと、AUTO が hibernate_sequence テーブルを使う方式になることがある。AUTO_INCREMENT を使いたいなら IDENTITY を明示するのが確実
  • IDENTITY とバッチ INSERT: IDENTITY は INSERT するまで ID が分からないため、Hibernate の JDBC バッチ INSERT が効かなくなる。大量登録が多いならシーケンス方式を検討する
  • 複合主キー: 複合キー(<composite-id> / @EmbeddedId)は自動採番できないため、各キー項目をアプリ側で必ず代入する

確認方法

  1. 例外メッセージ末尾のクラス名から、対象エンティティを特定する
  2. そのエンティティの hbm.xml(<id> 内の <generator>)または @Id 周辺のアノテーションを確認する
  3. DB の DDL(SHOW CREATE TABLE テーブル名; など)で、ID 列に AUTO_INCREMENT やシーケンスの既定値があるか確認する
  4. hibernate.show_sql=true で発行される INSERT 文を表示し、ID 列が含まれるか・値が入っているかを見る

関連

Post Share
子ページ

子ページはありません

同階層のページ
  1. ids for this class must be manually assigned before calling save()
  2. Number of positional parameter types (1 does not match number of positional parameters (2)
  3. net.sf.hibernate.MappingException: No persister for ~
  4. net.sf.hibernate.QueryException: unexpected token: as [~]
  5. net.sf.hibernate.MappingException: Error reading resource
  6. IllegalArgumentException occurred while calling setter of