1

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

易于理解的是有关 Map<String, Entity> 阅读 Hibernate doc on collections的示例,或 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)]

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

例如,对于多值属性,我认为它包含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()
    }
4

2 回答 2

3

JPA 没有为您提供任何方法来映射任何集合的集合。您可以映射基元、对实体的引用、可嵌入对象和上述任何事物的集合。

那里的集合意味着集合、列表或地图;这里没有可以帮助您的多地图类型。

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

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

很遗憾没有办法做到这一点。我认为 JPA 规范的作者要么没有想到它,要么认为这是一个足够晦涩的极端案例,不值得处理。

于 2013-01-28T21:17:57.793 回答
1

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

您无法使用三个表和一个 Account 实体对其进行建模,因为您没有足够的标识符。该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 规范证实了这一点。

现在,您可以在两个表中仅使用一个Account实体来执行此操作,方法是将 Set 折叠为某种序列化形式并将其保存为二进制、XML 或其他形式。不确定这是否值得您努力,但您草拟的专栏 ( value varchar(45)) 几乎可以肯定不够长。

于 2013-01-28T21:05:24.577 回答