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]にあります。