web-dev-qa-db-ja.com

Springでプログラム的にHibernateテーブル/エンティティ定義を拡張する方法は?

永続性を必要とする機能を全面的に追加できるように、テーブル/エンティティ定義を拡張する方法(多分AOPを使用)があるかどうか知りたいです。

具体的に例を挙げて説明します。簡単なeコマースシステムがあるとします。

  • ユーザー
    • id
    • 名前
    • パスワード
  • 項目
    • id
    • 名前
    • 価格
    • 株式
  • ショッピングカート
    • id
    • ユーザーID
  • ShoppingCartItems
    • id
    • shopping_cart_id
    • item_id
  • 注文
    • id
    • shopping_cart_id
  • 購入
    • id
    • order_id
    • 請求書番号

今、私たちが考えるとしましょう:

ねえ、すべてのエンティティにタイムスタンプを付けるフィールドを追加できたら素晴らしいと思いませんか?

次のようなことがわかります。

  • ユーザー登録時
  • アイテムが作成されたとき
  • ショッピングカートが作成されたとき
  • アイテムがショッピングカートに追加されたとき
  • ご注文時
  • 注文が処理されて支払われたのはいつですか

今、あなたは別の素晴らしいアイデアを持っています:

すべてのエンティティのステータスフィールドがあり、アイテムをソフト削除したりアーカイブしたりできる場合はどうでしょうか。

そしてさらに別のアイデア:

重要でないデータをシリアル化するmetaというテキストフィールドがあったとしても、データベースを変更することなく、機能を追加するのに役立ちます。

私が考えることができる解決策があります(リレーショナル整合性要件を満たす):

  1. もちろん、前に戻ってテーブルを変更し、実装を変更して、作成/更新時に、timestampと同じ、statusと同じ、meta

  2. そして、やや複雑ですが非常にモジュール化された方法です。ある種のプラグインを作成し、フィールドを処理するテーブルを作成し、エンティティごとに1つのピボットを作成します。そして、イベントを通じて、拡張データを既存のエンティティに適宜関連付けます。

しかし、もしあなたが実行時にプログラムでテーブル定義を拡張し、機能を注入するためにAOPのようなものを通して永続化動作を追加するようにHibernateに伝えることができたらどうでしょう。

私は実際にDoctrine=で最初のこと(プログラムで既存の実装の外にテーブル定義を拡張する)を1回成功させましたが、Hibernateを学び始めたばかりです。OTOH、Springを学び始めたばかりです。 AOPの基本を知っているので、少なくとも私の頭の中で、テーブルの定義を理解しているHibernateの構成プロセスを傍受することは可能です。

つまり、これはSpringとHibernateで可能ですか?、どうやって?、例を提供できますか?

補遺:問題は、これを複数回実行する必要があることであり、どこからでも発生する可能性があります。クラスの拡張は制約が多すぎるため、AOPは多重継承の問題に対する私の答えでしたが、以前はイベントでそれを実行し、同じようにうまく機能しました... IIRC問題は、私がDoctrineこれを行うには、モデルの説明が構成ファイルから実際のモデルに変換されている間のイベントを発生させます。

AOPは必須ではなく、そのようなイベントまたは依存性注入メカニズムがまだ導入されていない場合のアプローチのアイデアにすぎません。別のアプローチはミックスインかもしれませんが、おそらく最もエレガントなソリューションとしてAOPに戻ります。

ここでの主な要件は、元のモデルのコードを変更しないことです。

免責事項:私はAOPの専門家ではありませんが、概念にかなり精通しています。

7
dukeofgaming

私はあなたのユースケースが何であるか理解するのにかなり苦労しています。あなたは質問を考え出すのではなく、解決策を考え出します。

あなたの質問はmetadataとそれを保存する場所についてのようです。

次のようなことがわかります。

  • ユーザー登録時
  • アイテムが作成されたとき
  • ショッピングカートが作成されたとき
  • アイテムがショッピングカートに追加されたとき
  • ご注文時
  • 注文が処理されて支払われたのはいつですか

ユースケースによっては、ビジネスデータと一緒にメタデータを保存することは完全に理にかなっています。それに応じてデータベーススキームを変更する必要があります。

I)シンプルでわかりやすいユースケース

それから、createdcreatedbymodifiedmodifiedbyなどの情報を持つBaseItemを自然に拡張し、いくつかの列を他のオブジェクトに追加します。ユーザーのlastseen。これにより、これらの値からいくつかの質問を導き出すことができます。

アイテムがショッピングカートに追加されたとき

orderorderpositionがある場合、createdorderpositionは、エントリがaddedであったことを示します。

このユースケースでAOPを使用する理由がわかりません。

II)DataminingおよびMonitoringのより高度なユースケース

II a)モニタリング

簡単なeコマースシステムがあるとしましょう

実際にeコマースシステムがあり、例の目的だけでなく、 AppDynamics または New Relic のような監視に興味があるとしましょう。

このような高度な計測機能を使用すると、アプリケーションのあらゆるポイントを監視できます。 New Relicについて話すことはできませんが、AppDynamicsを自分で使用しました。 AppDynamicsを使用すると、Information Points、ここでは、メソッドが呼び出されるたびに追跡できます。 orderSubmit

ユースケースが、パフォーマンスの向上や単純なカウンターの場合にシステムの動作を追跡することである場合、それはコードに触れることなく、簡単な方法です。

しかし、そのようなシステムの主なポイントは、アプリケーションにhealth情報を提供することです。あなたがeコマースビジネスを運営しているなら、とにかくそれをお勧めします。ダウンタイムはお金の損失です。

II b)データマイニング

データマイニングのメタデータに興味がある場合はIIaは間違った道です。収集するメタデータの規模に応じて、別のデータストアに配置する必要があります。

すでに述べたように、アプリケーションにフックするにはいくつかの方法があります。

  • ビジネスコードを編集して、外部システムに単純なメソッド呼び出しを追加するだけです。

プロ:それは単純明快です。誰もが知っている、あなたがしていること

短所:コードベースを散らかす

  • aOPの使用-あなたが提案したように

プロ:それは、そのような横断的な懸念に対処するための洗練されたアプローチです。既存のコードに手を加える必要はありません

短所:経験の浅いプログラマー向けのコードを理解しづらくします-物事が魔法のように発生するようです

その間に何かあります。

単純さと読みやすさのために、私はあなたが別の種類の「ロギング」を持っているという事実にもかかわらず、最初の解決策に行きます

追記:I)とII)を一緒に行うことを妨げるものは何もありません:]

1
Thomas Junk

再びホイールを発明しないでください!

Hibernateにはプラグインがあります。プラグインを使用してください! Enver and Auditing に興味があるかもしれません。

これはあなたが探しているものの例です:

AuditReader reader = AuditReaderFactory.get(entityManager);

Person person2_rev1 = reader.find(Person.class, person2.getId(), 1); 
                            // version 1 is from archive
assert person2_rev1.getAddress().equals(new Address("Grimmauld Place", 12));

Address address1_rev1 = reader.find(Address.class, address1.getId(), 1);
assert address1_rev1.getPersons().getSize() == 1;
1
Peter Rader

Hibernateには、 interceptors を使用して対応できるイベントがあります。これは不完全なイベントのリストです:

  • onDelete
  • onSave
  • インスタンス化する
  • afterTransactionCompletion

Org.hibernate.Interceptorを見て、Interceptorを使用して対応できるイベントの完全なリストを確認してください。

古典的な使用法の単純化された例は、 "lastmodified"を設定する監査インターセプターです。

public class AuditInterceptor extends EmptyInterceptor {
    @Override public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) throws CallbackException {
        if (entity instanceof AuditedEntity) {
            int index = findElementIndex(propertyNames, "dateModified");

            if (index >= 0) {
                state[index] = new Date();

                return true; // entity has been modified
            }
        }

        return false; // entity was not modified
    }

    private int findElementIndex(String[] propertyNames, String element) {
        for (int i = 0; i < propertyNames.length; i++) {
            if (element.equals(propertyNames[i])) {
                return i;
            }
        }

        return -1;
    }
}

インターセプターはあなたのニーズに十分ではないかもしれません。幸いなことに、Hibernateのアーキテクチャは完全にプラグイン可能です-ほとんどの機能は "Services" で構成されています。サービスの役割(インターフェース)を実装するサービス実装。デフォルトの実装をカスタムの実装で上書きしたり、まったく新しいサービスを追加したりすることもできます。しかし、これはより複雑で、Hibernateの将来のバージョンでは機能しなくなる可能性があります。できるだけインターセプターに固執することをお勧めします。

1
qbd

私が知る限り、あなたが説明していることはEmbeddableクラスで簡単に実装でき、AOPは必要ありません。

@Embeddable
class Audit {
  Timestamp created;
  String createdBy;
  String updatedBy;
}

次に、エンティティ内に埋め込み可能なオブジェクトを追加するだけです。

@Entity
class Order {
   @Embedded
   Audit audit;
}

継承を使用すると、これを一連のエンティティで使用できるようにすることもできます。

@MappedSuperClass
class MotherOfAllEntities {
   @Embedded
   Audit audit;
}

@Entity
class ChildClass extends MotherOfAllEntities {
}

追加する情報が単なる監査情報である場合、投稿で示唆しているように、 Hibernate Envers のようなものを使用することを検討できます。これは、質問で明らかに示唆していることの実装のようです

@Entity
@Audited
class Order {
}

Enversは、エンティティに関する監査情報を他のデータ構造に確実に格納します。

Enversは、最初からやりたいことのようなものの実装のように見えるので、コードを見て、どのように実行したかを確認することもできますが、説明したことを実装する場合は、私には、バズーカでハエを殺しているように聞こえます。

1
edalorzo