エンティティが登録、更新、削除された履歴を管理します。
Hibernate Enversは、履歴管理テーブル全体を管理するエンティティ(デフォルトでRevinfo)をいて、自動採番されるid(プロパティ名はrev)と履歴を永続化した日時(プロパティ名はrevtstmp)の2プロパティを持っています。
エンティティ(後述のClient)の登録、更新、削除された履歴を管理するエンティティ(後述のClientHistory。デフォルトのクラス名はClientAud)が履歴管理するプロパティに加えて、Revinfoのid(デフォルトのプロパティ名はrev)とどのようなイベントが行われたかのフィールド(デフォルトのプロパティ名はrevtype)を持っています。ちなみにrevは、Revisionの略です。
やること
hibernate-enverを依存関係に追加する
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-envers</artifactId> </dependency>
@Auditedをエンティティクラス(またはエンティティの特定のフィールド)に付与する
今回は、Clientエンティティを使って説明します。
Client.java
@Entity @Audited public class Client implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Getter private Integer id; @Getter @Setter private String name; @Embedded @Getter @Setter private Address address; }
Address.java
@Embeddable @Getter @Setter @Audited public class Address implements Serializable { private int postalCode; private String address1; private String address2; }
ここでClientエンティティの履歴を管理したいのでクラスに対して@Auditedを付与します。
Clientエンティティの履歴を管理するエンティティは、デフォルトでClientAudとなります。Audは、Auditの略ですがわかりずらいのでClientHistoryにします。また、ClientHistoryのプロパティ名も、それぞれrevをauditId、revtypeをauditTypeに変更します。application.propertiesに次のように書けば変更できます。
spring.jpa.properties.org.hibernate.envers.audit_table_suffix=_history spring.jpa.properties.org.hibernate.envers.revision_field_name=audit_id spring.jpa.properties.org.hibernate.envers.revision_type_field_name=audit_type
テーブルを準備する
Flywayを使うので、sqlファイルに次のように書きます。
これで、Clientと対になるclientテーブル、ClientHistoryと対になるclient_historyテーブル、履歴管理テーブル全体の管理をするrevinfoが作られます。
create table client ( id integer not null auto_increment, name varchar(255), postal_code integer not null, address1 varchar(255), address2 varchar(255), primary key (id) ) engine=InnoDB; create table client_history ( id integer not null, name varchar(255), postal_code integer, address1 varchar(255), address2 varchar(255), audit_id integer not null, audit_type tinyint, primary key (id, audit_id) ) engine=InnoDB; create table revinfo ( rev integer not null auto_increment, revtstmp bigint, primary key (rev) ) engine=InnoDB; alter table client_history add constraint FK_client_history_revinfo foreign key (audit_id) references revinfo (rev);
できたテーブルがこれです。
登録、更新、削除やってみる
登録
Hibernate: insert into client (address1, address2, postal_code, name) values (?, ?, ?, ?) Hibernate: insert into revinfo (revtstmp) values (?) Hibernate: insert into client_history (audit_type, address1, address2, postal_code, name, id, audit_id) values (?, ?, ?, ?, ?, ?, ?)
作られたデータ
"client" : { "id": 1, "name": "hey,inc", "postalCode": 1500011, "address1": "東京都", "address2": "渋谷区東3丁目16番3号 エフ・ニッセイ恵比寿ビル4階" } "clientHistory": { "id": 1, "name": "hey,inc", "postalCode": 1500011, "address1": "東京都", "address2": "渋谷区東3丁目16番3号 エフ・ニッセイ恵比寿ビル4階", "audit_id": 1, "audit_type": 0 } "revinfo": { "rev": 1, "revtstmp": 1528214457258 }
更新
Hibernate: update client set address1=?, address2=?, postal_code=?, name=? where id=? Hibernate: insert into revinfo (revtstmp) values (?) Hibernate: insert into client_history (audit_type, address1, address2, postal_code, name, id, audit_id) values (?, ?, ?, ?, ?, ?, ?)
作られたデータ
"client" : { "id": 1, "name": "Coiney,inc", "postalCode": 1500011, "address1": "東京都", "address2": "渋谷区東3丁目16番3号 エフ・ニッセイ恵比寿ビル4階" } "clientHistory": { "id": 1, "name": "Coiney,inc", "postalCode": 1500011, "address1": "東京都", "address2": "渋谷区東3丁目16番3号 エフ・ニッセイ恵比寿ビル4階", "audit_id": 2, "audit_type": 1 } "revinfo": { "rev": 2, "revtstmp": 1528214465266 }
削除
Hibernate: delete from client where id=? Hibernate: insert into revinfo (revtstmp) values (?) Hibernate: insert into client_history (audit_type, address1, address2, postal_code, name, id, audit_id) values (?, ?, ?, ?, ?, ?, ?)
作られたデータ
"clientHistory": { "id": 1, "name": null, "postalCode": null, "address1": null, "address2": null, "audit_id": 3, "audit_type": 2 } "revinfo": { "rev": 3, "revtstmp": 1528214465681 }
ここでauditTypeの0、1、2は何ですか?という疑問が残ってるかと思いますが、それぞれ次の通りです。
0 → 登録
1 → 更新
2 → 削除
イベントにあったタイプが与えられます。
Hibernate Enversで履歴管理が楽になりました。現場からは以上でした。