如何使用 JPA 持久化 Map<Entity, Integer>?

Posted

技术标签:

【中文标题】如何使用 JPA 持久化 Map<Entity, Integer>?【英文标题】:Howto Persist a Map<Entity, Integer> with JPA? 【发布时间】:2011-09-28 04:14:58 【问题描述】:

我正在迁移一些具有两个实体(Progress 和 PerformanceRating)的代码,这两个实体通过多对多关系相关联。每个 PerformanceRating 都有多个 Progress,每个 Progress 类型可以分配给多个 PerformanceRating。此外,每个 PerformanceRating->Progress 都有一个与进度相关的附加“数量”值。

当前 PerformanceRating 对象包含一个 Map,表示分配给 PerformanceRating 对象的每个 Progress 类型的进度“数量”。

编码如下:

@Entity
class PerformanceRating
....
....
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "performance_rating_progress_reward", joinColumns =  @JoinColumn(name = "id", nullable = false, updatable = false) , inverseJoinColumns =  @JoinColumn(name = "amount", nullable = false, updatable = false) )
public Map<Progress, Integer> getRewardAmountByProgressMap() 
    return this.rewardAmountByProgressMap;

但是,当我启动 JBoss(使用 hibernate 3.6/JTA/JPA)时,我收到以下错误:

Use of @OneToMany or @ManyToMany targeting an unmapped class: fwl.domain.model.PerformanceRating.rewardAmountByProgressMap[java.lang.Integer]

我发现了一个类似的线程 (Persist a Map<Integer,Float> with JPA),但该线程似乎处理非实体类型。在像我这样的情况下,我正在寻找实体/值类型映射,正确的语法是什么?

有没有办法在 Hibernate/JPA2 中做到这一点?

谢谢,

埃里克

【问题讨论】:

JPA 不适合 Map 的情况。 JDO 是唯一处理这种情况的持久性规范 感谢您的提示。我不了解 JDO,所以我花了一点时间阅读它,我觉得它很有趣。如果我在 6 个月前知道这一点,我会推动 JDO 实现而不是 JPA 实现。听起来它有很多好处。一定会在未来的项目中牢记这一点。 【参考方案1】:

你得到的错误:

使用@OneToMany 或@ManyToMany 定位未映射的类:fwl.domain.model.PerformanceRating.rewardAmountByProgressMap[java.lang.Integer] em>

涉及到这样一个事实,即使用 @OneToMany@ManyToMany 之类的注释,您说声明类 (PerformanceRating) 与地图的 存在多对多关系, Integer 这是

您的地图的值应该是一个实体,它的键应该是您可以识别地图包含的实体之一的 id(实际上,键必须是唯一的,我认为,它不必是一个实际的 ID)。


我真的不知道你的桌子长什么样,但如果你的PerformanceRating(为了方便起见,我们就叫它Rating)看起来像这样:

rating
============
id               int(11) PK

和你的Progress 表是这样的:

progress
============
id               int(11) PK
amount           int(11)

有一个这样连接这些的表:

progress_has_rating
============
progress_id      int(11) PK
rating_id        int(11) PK

那么您可以通过以下方式映射它们:

@Entity @Table class Rating 
  @Id long id;
  @ManyToMany(targetEntity = Progress.class,
              cascade = CascadeType.ALL,
              fetch = FetchType.EAGER)
  @JoinTable(name = "progress_has_rating",
             joinColumns = @JoinColumn(name = "rating_id", 
                                        referencedColumnName = "id"),
             inverseJoinColumns = @JoinColumn(name = "progress_id",
                                               referencedColumnName = "id"))
  Set<Progress> progresses;


@Entity class Progress 
  @Id long id;
  @Basic long amount;

(很可能我在 @JoinColumn 注释中切换了列名,这实际上会起作用;我总是切换它们。)

(编辑:是的,我已经切换了它们——已修复。)


如果您的amount 属性在您的连接表中,那么您还需要为此创建一个实体类。我认为不可能绕过那个

如果你真的想使用地图,那么 Hibernate 可以做到这一点。请参阅Collection mapping 部分(特别是第 7.2.2.2 部分)了解如何映射 maps。不过,地图中的值需要是某种实体。

【讨论】:

感谢您的澄清。这有点证实了我所担心的。我对代码或表格的结构也不满意,但我不负责清理,因为这将是一项严肃的工作。如果可能的话,我只是想在不改变功能或结构的情况下清理它以使其工作,并且当前的映射显然被破坏了。不幸的是,这不仅仅是我需要的一套。 “金额”字段实际上存储在联接表中,而不是进度表中。 这很不幸,但并非没有先例。 :) 但是,您确实无法绕过创建实体类来表示您的连接表。查看这些链接here 和here,也许它们可以帮助您。干杯!

以上是关于如何使用 JPA 持久化 Map<Entity, Integer>?的主要内容,如果未能解决你的问题,请参考以下文章

JPA 最佳实践? [关闭]

Spring Boot JPA CrudRepository - 实体未附加到persistencecontext

Java JPA:如何跨 HTTP 请求保持会话活动?

JPA Map<String,String> 映射

如何使用 JPA 存储通用实体?

Spring Data JPA