データ]

  • リンク:/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]にあります。