如何休眠注释地图<string,set <string =“”>&gt;或Map <string,map <string,=“”string =“”>&gt; </string,> </string,>

时间:2013-01-28 19:51:44

标签: java hibernate mapping

使用Hibernate Core 4.1.7,JPA注释,java 1.7

关于Map&lt; String,Entity&gt;的例子很容易。读取Hibernate doc on collections或Map&lt; String,String&gt; here (stackoverflow)

很难找到关于Map<String, Set<String>>的例子(或者甚至是Map&lt; String,Map&lt; String,String&gt;&gt;只是为了好奇关于菊花chaning)为什么我在这里提出这个问题。

我想要的只是保存包含命名的多值属性(=帐户属性)的实体(帐户)。

我已经使用了3种实体类型:Account -> @OneToMany -> AccountAttribute -> @OneToMany -> AccountAttributeValue

但是使用我自己的类包装本机Java类型对我来说似乎有点愚蠢。克隆Map&lt; String,String&gt;例如我想要像

这样的东西
@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)]

作为数据库布局,我创建了2个表。   - 表Account只有一个指向外键的键   - 表Attribute在每行中包含命名值。

E.g。对于多值属性我认为它包含 2行,但fkAccountname相同但value不同 - 是的,我可以拥有归一化甚至更多,但我想在可接受的时间内读取我的数据: - )

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 
  ...

任何提示或备用数据库布局提案均受到赞赏。

编辑 - 已解决
汤姆的解决方案(据我所知)为我工作 谢谢你们,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()
}

和新

    @Embeddable
    public class AccountAttribute {

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

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

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

2 个答案:

答案 0 :(得分:3)

JPA没有为您提供任何方式来映射任何集合的集合。您可以映射基元,对实体的引用,嵌入式以及任何前述事物的集合。

集合表示集合,列表或映射;这里没有多图类型可以帮助你。

因此,遗憾的是没有办法准确映射你想要映射的结构。

我认为你最接近的可能是定义一个包含名称和值的可嵌入类Attribute,然后映射Set<Attribute>。然后,您可以将其转换为代码中的Map<String, Set<String>>

遗憾的是,没有办法做到这一点。我认为JPA规范的作者要么没有想到它,要么认为这是一个不起眼的案例,不值得处理。

答案 1 :(得分:1)

有两种方法可以在数据库中对此进行建模,这取决于您需要多少个表。如果您想要三个,那么您的工作方式基本上是正确的,尽管您可以将其修剪为两个实体(AccountAccountAttribute),其中AccountAttribute包含一组值。

您无法使用三个表和一个帐户实体对其进行建模,因为您没有足够的标识符。 VALUE表必须有一个由帐户ID和某种属性键组成的复合键,您的表必须如下所示:

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

如果AccountAttribute是实体,则它具有ID。如果没有,它没有,那么你如何键入ACCOUNT_ATTRIBUTE_VALUE表?

这可以通过JPA规范得到证实,如other answer中所述。

现在,您可以在只有Account实体的两个表中执行此操作,方法是将该Set压缩为某个序列化形式并将其保存为二进制,XML或其他任何形式。不确定这是否值得你付出努力,但你勾勒出的专栏(value varchar(45))几乎肯定不够长。