0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

enumの既定ORDINAL保存で外部ステータス照会が一致しない理由を実際にデバッグする

0
Posted at

はじめに

JPAで出荷状態を保存し、アプリケーションから再読込するとSHIPPEDを正しく返します。それでも、DBを直接照会する帳票や外部連携では状態コードが一致しないことがあります。原因は、JPAの再読込だけを確認し、物理列に何が保存されているかを契約として検証していない点です。

この記事では、ShipmentStatus.SHIPPEDを保存したとき、shipment.status列にも文字列"SHIPPED"を保存する契約を、Spring Data JPAとH2の再現教材で確認します。@Enumeratedを明示しないenumフィールドは、@EnumeratedValueを持たない場合に既定でEnumType.ORDINALとなります。Jakarta Persistence APIのEnumerated

問題を最小再現する

出荷エンティティの状態は、次のように保存されていました。

@Entity
public class Shipment {
    private ShipmentStatus status;
}

ShipmentStatusは次の固定値です。

public enum ShipmentStatus {
    READY,
    SHIPPED
}

この実装では、JPAを通した再読込は成功します。しかし、外部照会に使うstatus列の値は文字列ではなく整数になります。

観測点 期待 バグ状態
JPAで再読込した状態 SHIPPED SHIPPED
JDBCで読むshipment.status "SHIPPED" "1"
外部照会用の文字列コード 一致する 一致しない

失敗する契約テストでは、JPAの再読込値とJDBCで読む列値を別々に確認します。

assertAll(
    () -> assertEquals(ShipmentStatus.SHIPPED, reloadedStatus),
    () -> assertEquals("SHIPPED", storedStatus)
);

バグ状態では二つ目だけが失敗し、expected: <SHIPPED> but was: <1>と観測できます。これにより、保存自体の失敗ではなく、保存形式の不一致へ原因を絞れます。

原因を直接観測する

ShipmentStatus.SHIPPED.ordinal()1です。別の出荷を保存してからJDBCで物理列を読むと、保存値はこの序数と一致しました。

assertEquals(
    ShipmentStatus.SHIPPED.ordinal(),
    storedStatus
);

EnumType.ORDINALはenumを整数として保存し、EnumType.STRINGは文字列として保存します。Jakarta Persistence APIのEnumType @Enumeratedの既定値もORDINALです。つまり、JPAの変換が成功しているために問題が隠れているのではなく、enum保存形式を明示していないことが直接原因です。

最小修正

対象フィールドに保存形式を一つ追加します。

@Enumerated(EnumType.STRING)
private ShipmentStatus status;

この修正後、JPAの再読込とJDBCで読む物理列はどちらもSHIPPEDを表します。アプリケーションのDTO、外部HTTP連携、全enumの一括設定を変更する必要はありません。

再発を防ぐ確認

再発防止には、ORMを通した再読込だけでなく、今回の契約に対応する物理列または外部出力を独立して確認します。本教材では、次の二つのテストを残します。

  1. ShipmentRepositoryTestは、JPAの再読込値とJDBCの物理列がともにSHIPPEDであることを検証します。
  2. EnumStorageObservationTestは、STRING指定で物理列へ列挙名が保存されることを直接検証します。

この例は新規に保存する単一enumフィールドの形式に限定しています。既存整数データの移行、独自コードを持つenum、AttributeConverter、DBネイティブenum、enum名変更時の互換性は、別途設計・検証が必要です。

実行できる教材

バグ状態、直接観測、最小修正、回帰テスト、実行証跡は、公開されているSpring Data JPA enum保存形式デバッグ教材にあります。バグコミット215fb8cでは契約テストの失敗を、修正コミット10ce3ba以降では全テストの成功を確認できます。

まとめ

JPAのenumを保存するとき、アプリケーションで再読込できることと、DBの物理値が外部契約に適合することは別の確認項目です。@Enumeratedを省略すると既定のORDINAL保存が選ばれるため、文字列コードを必要とする場合は@Enumerated(EnumType.STRING)を明示します。再読込と物理列を分けてテストすれば、ORMの内部変換で隠れる不一致を回帰テストで検出できます。

参考資料

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?