1

Hibernate Core 4.1.7、JPAアノテーション、java1.7を使用

簡単に説明できるのは、コレクションのHibernateドキュメントを読み取るMap <String、Entity> 、またはここにあるMap <String、String>(stackoverflow)の例です。

Map<String, Set<String>>ここでこの質問をする理由の例(または、デイジーチャニングについての好奇心のためにMap <String、Map <String、String >>でさえ)を見つけるのは難しいです。

名前付きの複数値のプロパティ(=アカウント属性)を含むエンティティ(アカウント)を保存するだけです。

私はすべて3つのエンティティタイプで作業しています。Account -> @OneToMany -> AccountAttribute -> @OneToMany -> AccountAttributeValue

しかし、ネイティブJavaタイプを自分のクラスでラップすることは、私には少しばかげているように思えます。Map <String、String>の例のクローンを作成します。

@Entity
public class Account {

    @Id @GeneratedValue(strategy = GenerationType.AUTO) 
    @Column(name="key")
    private Long key;

    @ElementCollection(fetch=FetchType.EAGER)
    @JoinTable(name = "Attribute",
            joinColumns = @JoinColumn(name = "fkAccount"))
    @MapKeyColumn(name = "name")
    // next line is working for Map<String, String>, but not here!
    @Column(name = "value")  
    private Map<String, Set<String>> attributes = new HashMap<String, Set<String>>();

    // ... omitting: constructor, getters, setters, toString()

}

それは私に
Initial SessionFactory creation failed: org.hibernate.MappingException: Could not determine type for: java.util.Set, at table: Attribute, for columns:[org.hibernate.mapping.Column(attributes)]

DBレイアウトとして、2つのテーブルを作成しました。-Account外部キーを指すキーを持つテーブル-Attribute各行に名前付きの値を含むテーブル。

たとえば、複数値の属性の場合、同じであるが異なる2行が含まれていると思いました-はい、さらに正規化することもできましたが、許容可能な時間内にデータを読み取りたいです:-)fkAccountnamevalue

CREATE  TABLE IF NOT EXISTS `foo`.`Account` (
  `key` INT NOT NULL AUTO_INCREMENT ,
  ...

CREATE  TABLE IF NOT EXISTS `foo`.`Attribute` (
  `fkAccount` INT NOT NULL ,
  `name` VARCHAR(45) NOT NULL ,
  `value` VARCHAR(45) NOT NULL 
  ...

ヒントや代替のDBレイアウトの提案をいただければ幸いです。

編集-
トムからの解決された解決策(私が理解している限り)私のために働いてくれてありがとうみんな、なんて経験、1日で解決策!

私の質問で上で述べたテーブルレイアウトは、このクラスで機能するようになりました。

@Entity
public class Account {
    /* ... omitting "key", see as above */

    /* NEW: now @CollectionTable 
            replaces @JoinTable / @MapKeyColumn / @Column  from above
    */
    @ElementCollection(fetch = FetchType.EAGER)
    @CollectionTable(name="AccountAttribute",
                     joinColumns=@JoinColumn(name="fkAccount"))
    private Set<AccountAttribute> attributes = null;

    // ... omitting: constructor, getters, setters, toString()
}

とNEW

    @Embeddable
    public class AccountAttribute {

        @Column(name="attributeName")
        private String attributeName = null;

        @Column(name="attributeValue")
        private String attributeValue = null;

        // ... omitting: constructor, getters, setters, toString()
    }
4

2 に答える 2

3

JPAは、何かのコレクションのコレクションをマップする方法を提供しません。プリミティブ、エンティティへの参照、埋め込み可能オブジェクト、および前述のもののコレクションをマップできます。

そこにあるコレクションとは、セット、リスト、またはマップを意味します。ここには、役立つマルチマップタイプはありません。

したがって、残念ながら、マップしたい構造を正確にマップする方法はありません。

Attribute最も近いのは、名前と値を含む埋め込み可能なクラスを定義してから、をマップすることだと思いますSet<Attribute>Map<String, Set<String>>次に、これをinコードに変換できます。

これを行う方法がないのは残念です。JPA仕様の作成者は、それについて考えていなかったか、対処する価値がないほどあいまいなコーナーケースだと思っていたと思います。

于 2013-01-28T21:17:57.793 に答える
1

データベースでこれをモデル化する方法は2つあり、必要なテーブルの数によって決まります。3つ必要な場合は、基本的に正しい方法ですが、値のセットを含む2つのエンティティ(AccountおよびAccountAttribute)にトリミングすることもできます。AccountAttribute

十分な識別子がないため、3つのテーブルと1つのアカウントエンティティだけでモデル化することはできません。テーブルには、アカウントIDとある種の属性キーで構成される複合キーが必要です。VALUEテーブルは次のようになります。

ACCOUNT (id, ...)
ACCOUNT_ATTRIBUTE(account_id, account_attribute_id, ...)
ACCOUNT_ATTRIBUTE_VALUE(account_id, account_attribute_id, value, ...)

AccountAttributeがエンティティの場合、IDがあります。そうでない場合はそうではないので、どのようにACCOUNT_ATTRIBUTE_VALUEテーブルにキーを設定しますか?

これは、この他の回答で述べられているように、JPA仕様によって裏付けられています。

Accountこれで、 Set inをシリアル化された形式に折りたたんで、バイナリやXMLなどとして永続化することにより、エンティティのみを含む2つのテーブルでこれを行うことができます。それがあなたにとって努力する価値があるかどうかはわかりませんが、あなたがスケッチした列(value varchar(45))はほぼ確実に十分な長さではありません。

于 2013-01-28T21:05:24.577 に答える