データベースの column1、column2、columnN を要素のコレクションにマップします。

StackOverflow https://stackoverflow.com/questions/1134507

質問

従来のデータベース テーブルには、C1、C2、C3、C100 または M1、M2、M3、M100 などの番号が付けられた列があります。
この列は BLOB データを表します。

このデータベースの内容を変更することはできません。

JPA Embeddable を使用して、すべての列を単一のフィールドにマップします。そして、埋め込み中に 100 個のオーバーライド アノテーションを使用して名前をオーバーライドします。

最近、Hibernate に切り替えましたが、次のようなことに気づきました。 ユーザーコレクションの種類 そして 複合ユーザータイプ. 。しかし、私に近いユースケースは見つかりませんでした。

Hibernate を使用して一部のユーザー タイプを実装し、列のバンドルをコレクションにマップできるようにすることは可能ですか? 追加のクエリなしで?

編集:
お気づきかと思いますが、列の名前はテーブルごとに異なる場合があります。この型を使用するたびにすべての @Column を指定する必要がなく、「LegacyArray」のような型を 1 つ作成したいと考えています。しかし、代わりに私は使用します

  @Type(type = "LegacyArrayUserType",
        parameters =
   {
      @Parameter(name = "prefix", value = "A"),
      @Parameter(name = "size", value = "128")
   })
   List<Integer> legacyA;

  @Type(type = "LegacyArrayUserType",
        parameters =
   {
      @Parameter(name = "prefix", value = "B"),
      @Parameter(name = "size", value = "64")
   })
   List<Integer> legacyB;
役に立ちましたか?

解決

これを行う方法はいくつか考えられます。

1.正規化されたテーブル構造をシミュレートするコレクション情報のビューを作成し、それをコレクションとして Hibernate にマップします。

既存のテーブルが と呼ばれると仮定します primaryentity, 次のようなビューを作成します。

-- untested SQL...
create view childentity as
(select primaryentity_id, c1 from primaryentity union
select primaryentity_id, c2 from primaryentity union
select primaryentity_id, c3 from primaryentity union
--...
select primaryentity_id, c100 from primaryentity)

Hibernate の観点から見ると、 childentity 外部キーを持つ正規化されたテーブルです。 primarykey. 。これのマッピングは非常に簡単です。ここで説明します。

このアプローチの利点は次のとおりです。

  • Hibernate の観点から見ると、テーブルは正規化されており、非常に単純なマッピングです。
  • 既存のテーブルは更新されません

欠点:

  • データは読み取り専用です。あなたのビューは更新可能な方法で定義できないと思います(間違っている可能性があります)
  • データベースへの変更が必要です。多くのビューを作成する必要がある場合があります

あるいは、DBA がデータベースにビューを追加することさえ許可しない場合、または更新を実行する必要がある場合は、次のようにします。


2.Hibernate を使用する 動的モデルマッピング機能 C1、C2、C3 プロパティを 地図, あなたのコードをいくつか用意してください ダオ レイヤーは、Map と Collection プロパティの間で適切な会話を行います。

私自身はこれを行ったことはありませんが、Hibernate ではテーブルを HashMap にマッピングできると思います。Hibernate でこれをどの程度動的に実行できるかはわかりません (つまり、単純にテーブル名を指定し、Hibernate にすべての列を自動的にマップさせるだけで済むでしょうか?) が、これを行うことも考えられる別の方法です。

ただし、このアプローチを使用する場合は、必ず データアクセスオブジェクト パターンを使用し、内部実装 (HashMap の使用) がクライアント コードから隠されていることを確認します。また、データベースに書き込む前に、コレクションのサイズが使用可能な列の数を超えていないことを必ず確認してください。

このアプローチの利点は次のとおりです。

  • データベースにはまったく変更がありません
  • データは更新可能です
  • O/R マッピングは比較的単純です

欠点:

  • 適切なタイプをマッピングするための DAO レイヤー内の多数の配管
  • 将来変更される可能性がある実験的な Hibernate 機能を使用します

他のヒント

個人的にはこのデザインは壊れているように思えます 第一正規形 リレーショナルデータベースの場合。C101 または M101 が必要な場合はどうなりますか?スキーマを再度変更しますか?それは非常に侵入的だと思います。

これに Hibernate を追加すると、事態はさらに悪化します。C101 または M101 を追加すると、Java オブジェクト、Hibernate マッピング、その他すべてを変更する必要があります。

C および M テーブルと 1:m の関係がある場合は、行を追加することで、先ほど挙げたケースに対処できます。Java オブジェクトには Collection<C> または Collection<M> が含まれています。Hibernate マッピングは 1 対多であり、変更されません。

おそらく、あなたのケースに一致する Hibernate の例が見つからないのは、それが推奨されていない設計だからかもしれません。

必要な場合は、おそらく見るべきです Hibernate コンポーネントのマッピング.

アップデート:これがレガシーであるという事実は、正式に指摘されています。私が第一正規形を持ち出したのは、質問を投稿した人のためであると同時に、将来この質問を見つけるかもしれない他の人のためでもあります。私は、このデザインが「良い」と無言で主張するような形で質問に答えたくありません。

Hibernate コンポーネントのマッピングを指摘することは適切です。なぜなら、探しているものの名​​前を知っていることが検索の際の鍵になるからです。Hibernate を使用すると、オブジェクト モデルをマップするリレーショナル モデルよりも細かくすることができます。非正規化スキーマを自由にモデル化できます (たとえば、より大きな Person オブジェクトの一部としての Name オブジェクトと Address オブジェクト)。それは彼らがそのようなテクニックに付けた名前にすぎません。他の例を見つけるのにも役立つかもしれません。

ここでの問題を誤解していたら申し訳ありませんが、私は Hibernate についてあまり知りません。しかし、データベースからの選択時に連結して、必要なものを取得することはできないでしょうか?

のように:

SELECT whatever
     , C1||C2||C3||C4||...||C100 AS CDATA
     , M1||M2||M3||M4||...||M100 AS MDATA
FROM ...
WHERE ...

(もちろん、RDBMSごとに連結演算子は異なります。)

[編集] を使用することをお勧めします CompositeUserType. ここに例があります. 。『Java Persistence With Hibernate』という本の 228f ページにも良い例があります。

これにより、Java で多数の列を単一のオブジェクトとして処理できるようになります。

マッピングは次のようになります。

@org.hibernate.annotations.Columns(columns = {
    @Column(name="C1"),
    @Column(name="C2"),
    @Column(name="C3"),
    ...
})
private List<Integer> c;

Hibernate は、通常のクエリ中にすべての列を一度にロードします。

あなたの場合、リストから int 値を次の固定数の列にコピーする必要があります。 nullSafeSet. 。疑似コード:

for (int i=1; i<numColumns; i++)
    if (i < list.size())
        resultSet.setInt(index+i, list.get(i));
    else
        resultSet.setNull(index+i, Hibernate.INTEGER.sqlType());

nullSafeGet リストを作成し、列が NULL の場合は要素の追加を停止する必要があります。安全性をさらに高めるために、列数を超えて拡張できない独自のリスト実装を作成することをお勧めします ( ArrayList そしてオーバーライド ensureCapacity()).

[編集2] すべての@Column注釈を入力したくない場合は、コードジェネレーターを使用してください。これは、名前と番号を指定して @Column(...) を System.out に出力するスクリプトと同じくらい簡単です。スクリプトが実行されたら、データをソースに切り取って貼り付けるだけです。

他の唯一の解決策は、内部 Hibernate API にアクセスして実行時にその情報を構築することですが、その API は内部のものであるため、多くの情報は非公開です。Java リフレクションを使用できます。 setAccessible(true) しかし、そのコードはおそらく Hibernate の次のアップデートまでは存続しないでしょう。

使用できます UserType■ 指定された数の列を任意の型にマップします。(たとえば) コレクションのサイズが常に既知の項目数によって制限されている場合、これはコレクションである可能性があります。

Hibernate を使用してからしばらく (3 年以上) いたため、かなり慣れてきましたが、非常に簡単だったことを覚えています。あなたの BespokeUserType クラスに渡される ResultSet水分補給する そこからあなたのオブジェクトを選択します。

私も Hibernate を使用したことがありません。

インタープリタ型言語で小さなプログラムを書くことをお勧めします (例: パイソン) を使用すると、文字列をコマンドであるかのように実行できます。手動でやりたいことを行うための面倒な作業を省くステートメントを作成できます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top