JPA、Hibernate、Spring Data JPAによる監査
データ]
-
リンク:/tag/hibernate/[休止状態]
-
リンク:/tag/jpa/[JPA]
1概要
ORMの文脈では、データベース監査とは、永続エンティティに関連するイベントの追跡とログ記録、または単にエンティティのバージョン管理を意味します。
SQLトリガーに触発されて、イベントはエンティティの挿入、更新および削除操作です。データベース監査の利点は、ソースバージョン管理によってもたらされるものと似ています。
アプリケーションに監査を導入するための3つのアプローチを説明します。まず、標準のJPAを使用して実装します。次に、Hibernateが提供するものとSpring Dataが提供するものの2つの、独自の監査機能を提供する2つのJPA拡張について見ていきます。
この例で使用されるサンプル関連エンティティ
Bar
と
Foo、
は次のとおりです。
リンク:/uploads/Screenshot__4.png[]
2 JPAによる監査
JPAには監査APIが明示的に含まれていませんが、機能はエンティティライフサイクルイベントを使用して実現できます。
2.1.
@ PrePersist、
@ PreUpdate
、
__
@ PreRemove
__
JPA
Entity
クラスでは、特定のエンティティライフサイクルイベント中に呼び出されるコールバックとしてメソッドを指定できます。対応するDML操作の前に実行されるコールバックに関心があるので、
@ PrePersist
、
@ PreUpdate
、および
@ PreRemove
コールバックアノテーションがあります。
@Entity
public class Bar {
@PrePersist
public void onPrePersist() { ... }
@PreUpdate
public void onPreUpdate() { ... }
@PreRemove
public void onPreRemove() { ... }
}
内部コールバックメソッドは常に戻り値を取り、引数を取りません。
それらは任意の名前と任意のアクセスレベルを持つことができますが、そうではいけません。
JPAの
@ Version
アノテーションは厳密には私たちのトピックとは無関係であることに注意してください – それは監査データよりも楽観的ロックに関係しています。
2.2. コールバックメソッドの実装
ただし、この方法には大きな制限があります。 JPAに記載されているように
一般に、ポータブルアプリケーションのライフサイクルメソッドは、EntityManager
または
Query__オペレーションを呼び出したり、他のエンティティインスタンスにアクセスしたり、同じ永続コンテキスト内で関係を変更したりしないでください。
ライフサイクルコールバックメソッドは、それが呼び出されたエンティティの非関係状態を変更することがあります。
監査フレームワークがない場合は、データベーススキーマとドメインモデルを手動で保守する必要があります。単純なユースケースでは、「エンティティの非関係状態」のみを管理できるため、エンティティに2つの新しいプロパティを追加しましょう。
operation
プロパティは実行された操作の名前を格納し、
timestamp
プロパティは操作のタイムスタンプ用です。@Entity public class Bar { //... @Column(name = "operation") private String operation; @Column(name = "timestamp") private long timestamp; //... //standard setters and getters for the new properties //... @PrePersist public void onPrePersist() { audit("INSERT"); } @PreUpdate public void onPreUpdate() { audit("UPDATE"); } @PreRemove public void onPreRemove() { audit("DELETE"); } private void audit(String operation) { setOperation(operation); setTimestamp((new Date()).getTime()); } }
そのような監査を複数のクラスに追加する必要がある場合は、
@ EntityListeners
を使用してコードを一元管理できます。例えば:@EntityListeners(AuditListener.class) @Entity public class Bar { ... }
public class AuditListener { @PrePersist @PreUpdate @PreRemove private void beforeAnyOperation(Object object) { ... } }
===
3休止状態のエンバー
Hibernateでは、監査を達成するために
Interceptors
と
EventListeners
、そしてデータベーストリガーを利用することができます。しかし、ORMフレームワークはEnversを提供します。これは、永続クラスの監査とバージョン管理を実装するモジュールです。====
3.1. Enversを始めよう
Enversを設定するには、
hibernate-envers
JARをクラスパスに追加する必要があります。<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-envers</artifactId> <version>${hibernate.version}</version> </dependency>
その後、
@ Entity
(エンティティ全体を監査する)または特定の
__ @ Column
s(特定のプロパティのみを監査する必要がある場合)に
@ Audited__アノテーションを追加するだけです。@Entity @Audited public class Bar { ... }
Bar
は
Foo
と1対多の関係にあることに注意してください。この場合、
Foo
に
@ Audited
を追加するか、
Bar
の関係のプロパティに
@ NotAudited
を設定して、
Foo
を監査する必要があります。@OneToMany(mappedBy = "bar") @NotAudited private Set<Foo> fooSet;
====
3.2. 監査ログテーブルの作成
監査表を作成する方法はいくつかあります。
hibernate.hbm2ddl.auto
を
create
、
create-drop
、または
update
に設定します。つまり、Enversはそれらを自動的に作成する
** o
__rg.hibernate.tool.EnversSchemaGenerator
__を使用してプログラムによるデータベーススキーマの完成
** Antタスクを使用して適切なDDL文を生成する
マッピングからデータベーススキーマを生成するためにMavenプラグインを使用する
Enversスキーマをエクスポートする(Juploなど)(Hibernate 4以降で動作します)
最も直接的な方法なので、最初の方法を使用しますが、
hibernate.hbm2ddl.auto
を使用することはプロダクション上安全ではないことに注意してください。この場合、
bar
AUD
テーブルと
foo
AUD
テーブル(
Foo
を
@ Audited
に設定した場合)も自動的に作成されます。監査テーブルは、
REVTYPE
(値は追加用の “0”、更新用の “1”、エンティティの削除用の “2”)と
REV
の2つのフィールドを持つエンティティのテーブルからすべての監査対象フィールドをコピーします。これらに加えて、
REVINFO
という名前の追加のテーブルがデフォルトで生成されます。それは2つの重要なフィールド、
REV
と
REVTSTMP
を含み、すべてのリビジョンのタイムスタンプを記録します。ご想像のとおり、
bar
AUD.REV
と
foo
AUD.REV
は実際には
REVINFO.REV.
に対する外部キーです。====
3.3. Enversの設定
Enversプロパティは他のHibernateプロパティと同じように設定できます。
たとえば、監査テーブルのサフィックス(デフォルトは ”
__AUD
“)を ”
__AUDIT
LOG
“に変更しましょう。対応するプロパティ
org.hibernate.envers.audit
table
suffix__の値を設定する方法は次のとおりです。Properties hibernateProperties = new Properties(); hibernateProperties.setProperty( "org.hibernate.envers.audit__table__suffix", "__AUDIT__LOG"); sessionFactory.setHibernateProperties(hibernateProperties);
利用可能なプロパティの全一覧はhttp://docs.jboss.org/envers/docs/#configuration[Enversのドキュメント]にあります。
====
3.4. エンティティ履歴へのアクセス
Hibernate基準APIを介してデータを照会するのと同じ方法で、履歴データを照会することができます。エンティティの監査履歴は
AuditReader
インタフェースを使用してアクセスできます。これは
AuditReaderFactory
を介して
EntityManager
または
Session
を開くことで取得できます。AuditReader reader = AuditReaderFactory.get(session);
Enversは監査固有のクエリを作成するために
AuditQueryCreator
(
AuditReader.createQuery()
によって返される)を提供します。次の行は、リビジョン#2で修正されたすべての
Bar
インスタンスを返します(ここで、
bar
AUDIT
LOG.REV = 2
)。AuditQuery query = reader.createQuery() .forEntitiesAtRevision(Bar.class, 2)
これは
Bar
のリビジョンを問い合わせる方法です。つまり、監査されたすべての状態にあるすべての
Bar
インスタンスのリストを取得することになります。AuditQuery query = reader.createQuery() .forRevisionsOfEntity(Bar.class, true, true);
2番目のパラメータがfalseの場合、結果は
REVINFO
テーブルと結合されます。それ以外の場合は、エンティティインスタンスのみが返されます。最後のパラメータは、削除された
Bar
インスタンスを返すかどうかを指定します。その後、
AuditEntity
ファクトリクラスを使用して制約を指定できます。query.addOrder(AuditEntity.revisionNumber().desc());
===
4 Spring Data JPA
Spring Data JPAは、JPAプロバイダの上に追加の抽象化層を追加することによってJPAを拡張するフレームワークです。このレイヤは、Spring JPAリポジトリインタフェースを拡張することでJPAリポジトリの作成をサポートします。
私たちの目的のために、あなたは
CrudRepository <T、IDはSerializable>
、一般的なCRUD操作のためのインターフェースを拡張することができます。リポジトリを作成して別のコンポーネントにインジェクトするとすぐに、Spring Dataによって実装が自動的に提供され、監査機能を追加する準備が整います。====
4.1. JPA監査を有効にする
はじめに、アノテーション設定による監査を有効にします。そのためには、
@ Configuration
クラスに
@ EnableJpaAuditing
を追加するだけです。@Configuration @EnableTransactionManagement @EnableJpaRepositories @EnableJpaAuditing public class PersistenceConfig { ... }
====
4.2. SpringのEntity Callback Listener
を追加するすでにご存じのとおり、JPAにはコールバックリスナクラスを指定するための
@ EntityListeners
アノテーションが用意されています。 Spring Dataは独自のJPAエンティティリスナクラス
AuditingEntityListener
を提供しています。それでは、
Bar
エンティティのリスナーを指定しましょう。@Entity @EntityListeners(AuditingEntityListener.class) public class Bar { ... }
監査情報は、
Bar
エンティティの永続化および更新時にリスナーによって取得されます。====
4.3. 作成日と最終更新日の追跡
次に、作成日と最終更新日を格納するための2つの新しいプロパティを
Bar
エンティティに追加します。プロパティには、それに応じて
@ CreatedDate
および
@ LastModifiedDate
アノテーションが付けられ、それらの値は自動的に設定されます。@Entity @EntityListeners(AuditingEntityListener.class) public class Bar { //... @Column(name = "created__date", nullable = false, updatable = false) @CreatedDate private long createdDate; @Column(name = "modified__date") @LastModifiedDate private long modifiedDate; //... }
一般に、プロパティを基本クラス(
@ MappedSuperClass
のアノテーション付き)に移動します。これは、すべての監査対象エンティティによって拡張されます。この例では、単純にするためにそれらを
Bar
に直接追加します。====
4.4. Spring Security
による変更の作者の監査アプリがSpring Securityを使用している場合は、いつ変更されたのかだけでなく、誰が変更したのかも追跡できます。
@Entity @EntityListeners(AuditingEntityListener.class) public class Bar { //... @Column(name = "created__by") @CreatedBy private String createdBy; @Column(name = "modified__by") @LastModifiedBy private String modifiedBy; //... }
@ CreatedBy
および
@ LastModifiedBy
という注釈が付けられた列には、エンティティを作成または最後に変更した主体の名前が移入されます。情報は
SecurityContext
s
Authentication
インスタンスから取得されます。注釈付きフィールドに設定される値をカスタマイズしたい場合は、
AuditorAware <T>
インターフェースを実装できます。public class AuditorAwareImpl implements AuditorAware<String> { @Override public String getCurrentAuditor() { //your custom logic } }
現在の主体を検索するために
AuditorAwareImpl
を使用するようにアプリを設定するには、
AuditorAwareImpl
のインスタンスで初期化された
AuditorAware
タイプのBeanを宣言し、そのBeanの名前を@EnableJpaAuditing(auditorAwareRef="auditorProvider") public class PersistenceConfig { //... @Bean AuditorAware<String> auditorProvider() { return new AuditorAwareImpl(); } //... }
===
5結論
監査機能を実装するための3つのアプローチを検討しました。
純粋なJPAアプローチは最も基本的なものであり、
ライフサイクルコールバックただし、エンティティの非関係状態を変更できるのはあなただけです。このメソッドで設定した設定はエンティティとともに削除されるため、
@ PreRemove
コールバックはこの目的には役に立ちません。
EnversはHibernateが提供する成熟した監査モジュールです。高いです
設定可能で、純粋なJPA実装の欠陥がありません。したがって、エンティティのテーブル以外のテーブルにログインするときに、削除操作を監査することができます。
Spring Data JPAアプローチはJPAコールバックを使った作業を抽象化
監査プロパティに便利な注釈を提供します。 Spring Securityと統合する準備も整いました。不利な点は、それがJPAアプローチの同じ欠陥を継承するため、削除操作を監査できないことです。
この記事の例はhttps://github.com/eugenp/tutorials/tree/master/persistence-modules/spring-data-jpa[a GitHub repository]にあります。