【问题标题】:Map<String, Entity> with composite key in EntityMap<String, Entity> 与 Entity 中的复合键
【发布时间】:2014-04-29 16:47:23
【问题描述】:

我有以下两个表:

TABLE A:
  INT      id
  VARCHAR  x
PRIMARY KEY (id)

TABLE B:
  INT      a_id
  VARCHAR  locale
  VARCHAR  z
PRIMARY KEY (a_id, locale)

这基本上是一个简单的 OneToMany 关系。表B 包含表A 中引用行的ID (a_id) 加上locale。这意味着:A 中的每个条目都可以在表 B 中具有 0..* 条目,每个条目都有一个不同的 locale 值。

我有以下两个类,它们应该代表那些表:

@Entity
@Table(name="A")
class A {
  @Id
  @Column(name="id")
  int id;

  @Column(name="x")
  String x;

  @OneToMany(mappedBy="a") // ???
  @MapKey... // ???
  Map<String, B> bMap;
}

@Entity
@Table(name="B")
class B {
  @ManyToOne
  @JoinColumn(name="a_id")
  A a;

  @Column(name="locale")
  String locale;

  @Column(name="z")
  String z;
}

现在缺少两件事:

  1. Map&lt;String, B&gt; bMap 的注释。我只是不知道我是否应该使用@MapKey@MapKeyColumn 以及如何映射到该复合键。如果我应该/必须使用@OneToMany

  2. B 类当然需要一个复合键。我应该使用@EmbeddedId 还是@IdClass

您能否提供一些此场景的示例代码?

谢谢!

【问题讨论】:

    标签: java hibernate jpa map jpa-2.0


    【解决方案1】:

    底部的工作解决方案


    我想,我现在已经设法把事情放在一起了。至少生成的 SQL 表看起来是正确的,但我仍然需要弄清楚如何完成级联保存...

    @Entity
    @Table(name="A")
    public class A {
       @Id @GeneratedValue(strategy=GenerationType.AUTO)
       long id;
    
       @Column(name="x")
       String x;
    
       @OneToMany(mappedBy="id.a", cascade=CascadeType.ALL, orphanRemoval=true)
       @MapKey(name="id.locale")
       Map<String, B> bMap = new HashMap<String, B>();
    }
    
    @Entity
    @Table(name="B")
    public class B {
       @EmbeddedId
       BPK id;
    
       @Column(name="z")
       String z;
    }
    
    @Embeddable
    public class BPK implements Serializable {
       @ManyToOne
       @JoinColumn(name="a_id")
       A a;
    
       @Column(name="locale")
       String locale;
    
       // equals + hashcode
    }
    

    当调用aRepository.findById(...)Hibernates 时:

    Hibernate: select * from A a where a.id=?
    

    这是正确的。

    但如果我调用aEntity.getBMap(),它总是会获取整个地图,即使我只想使用aEntity.getBMap().put("EN", someBObject) 并且不想从中读取任何数据。不过暂时没关系。

    现在我只是想弄清楚如何让级联储蓄发挥作用。做aEntity.getBMap().put("EN", someBObject); aRepository.save(eEntity);时我得到了

    org.hibernate.id.IdentifierGenerationException: null id generated for:class B
    

    我想我只是缺少一些 @EmbeddedId 或其字段的设置器。


    终于解决了:

    级联保存不知何故不适用于@EmbeddedId 复合键。所以我想了想,发现我可以改用@ElementCollection:).

    这就是我最终所做的:

    @Entity
    @Table(name="A")
    public class A {
       @Id @GeneratedValue(strategy=GenerationType.AUTO)
       long id;
    
       @Column(name="x")
       String x;
    
       @ElementCollection
       @CollectionTable(name="B", joinColumns=@JoinColumn(name="a_id"))
       @MapKeyColumn(name="locale")
       Map<String, B> bMap = new HashMap<String, B>();
    }
    
    @Embeddable
    public class B {
       @Column(name="z")
       String z;
    }
    

    休眠输出:

    A a = aRepository.findById(...)

    • 休眠:select * from A where id=?

    a.getBMap().put("EN", someBObject)

    • 休眠:select * from B where a_id=?

    aRepository.save(a)

    • 休眠:插入 B (a_id, locale, z) 值 (?, ?, ?)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-05-13
      • 1970-01-01
      • 2012-08-12
      • 1970-01-01
      • 1970-01-01
      • 2017-04-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多