PostgreSQL に対し、 JPA のエンティティからテーブルを自動生成して ID 列を自動採番する方法を試してみました。
今回は Spring Boot 1.3.0 を使っています。 JPA 実装は Hibernate 4.3.11.Final のようです*1。
JPA のエンティティがこんな感じ。
@Entity @Table(name = "account") public class Account { @Id @SequenceGenerator(name = "account_id_gen", sequenceName = "account_id_seq") @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "account_id_gen") private Long id; @Column(nullable = false) private String loginId; // getter/setter 略 }
GeneratedValue
に加え、SequenceGenerator
を指定するのがポイントっぽいです。オプションなしの GeneratedValue
のみの場合、 hibernate_sequence
というシーケンスが作られるようですが、さすがに明示的に指定したほうが良さそうです。
Spring Boot を使っているので、 application.yml
にデータベース接続定義を書きます。ユーザーやデータベース自体は予め作成しておくものとします。
spring: jpa: show-sql: true hibernate: ddl-auto: create-drop datasource: url: jdbc:postgresql://localhost:5432/sampledb driverClassName: org.postgresql.Driver username: testuser password: password
これでアプリケーションを起動し、 psql でデータベースの状態を確認します。以下のような感じでテーブルとシーケンスが作成されることが確認できます。
sampledb=> \d リレーションの一覧 スキーマ | 名前 | 型 | 所有者 ----------+----------------+------------+--------- public | account | テーブル | testuser public | account_id_seq | シーケンス | testuser (2 行) sampledb=> \d account テーブル "public.account" 列 | 型 | 修飾語 ----------+------------------------+------------------------------------------------------ id | bigint | not null default nextval('account_id_seq'::regclass) login_id | character varying(255) | not null インデックス: "account_pkey" PRIMARY KEY, btree (id)
シーケンスが作成され、 id 列のデフォルト値として設定されていることがわかります。この状態で JPA でレコード登録を行うと、以下のような SQL が出力されます。デフォルト値が使われていますね。
Hibernate: insert into account (login_id) values (?)
なお、 GenerationType.SEQUENCE
を指定した場合、 id 列のデフォルト値が設定されませんでした。
sampledb=> \d account テーブル "public.account" 列 | 型 | 修飾語 ----------+------------------------+---------- id | bigint | not null login_id | character varying(255) | not null インデックス: "account_pkey" PRIMARY KEY, btree (id)
しかしこの状態で JPA で登録を行ってもちゃんと登録されます。 SQL のログを見ると以下のようになっていました。一度シーケンスから値を取得してから insert するようです。検索すると SEQUENCE
を使ってる例が結構出てきますが、どっちがいいんだろう?
Hibernate: select nextval ('account_id_seq') Hibernate: insert into account (login_id, id) values (?, ?)
自動採番される id は RDBMS によって JPA のエンティティの定義方法が微妙に変わってくるところがちょっとめんどうですね。
*1:Spring Boot の場合とそうでない場合で挙動が違うかどうかは調べていません