【问题标题】:How to persist a custom map with Hibernate/JPA?如何使用 Hibernate/JPA 持久化自定义地图?
【发布时间】:2014-12-04 01:12:00
【问题描述】:

我正在尝试使用 Hibernate 和 JPA 注释将子类 HashMap 持久保存在数据库中。

import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MapKeyColumn;
import javax.persistence.Table;

@Entity
@Table(name = "module")
public class Module {

    @Id
    @GeneratedValue
    @Column(name = "id")
    private int id;

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

    @Column(name = "description")
    private String description;

    @Column(name = "version")
    private String version;

    @ElementCollection(fetch = FetchType.EAGER)
    @CollectionTable(name = "module_parameter")
    @MapKeyColumn(name = "key")
    @Column(name = "value")
    private Parameters params = new Parameters();

    // Getters & setters

}

这在尝试存储参数时给了我以下异常:

org.hibernate.AnnotationException: Illegal attempt to map a non collection as a @OneToMany, @ManyToMany or @CollectionOfElements: Module.params

当我将声明更改为

private Map<String, String> params = new Parameters();

public Parameters getParams() {
    return (Parameters) params;
}

我得到以下输出:

Hibernate: select module0_.id as id1_0_, module0_.description as descript2_0_, module0_.name as name3_0_, module0_.version as version4_0_ from module module0_
Hibernate: select params0_.Module_id as Module_i1_0_0_, params0_.value as value2_1_0_, params0_.key as key3_0_ from module_parameter params0_ where params0_.Module_id=?
Exception in thread "main" java.lang.ClassCastException: org.hibernate.collection.internal.PersistentMap cannot be cast to Parameters

如果我把它改成

private Map<String, String> params = new HashMap<>();

一切正常。

那么我如何告诉 Hibernate Parameters 是一个集合?

【问题讨论】:

  • FWIW 你上面的内容可以在我使用的 JPA 实现(DataNucleus)中开箱即用。也许一些 JPA impls 只提供对 Map、HashMap 等显式类型的支持?

标签: java hibernate jpa


【解决方案1】:

您必须创建一个实现UserCollectionType 接口的类,然后将该类提供给hibernate 以告知您要使用自定义集合。

查看这些链接以了解如何创建自定义集合:

How to map custom collection in JPA?

Hibernate: Custom Collection Types

【讨论】:

  • 这听起来很有趣,但它定义了一些不适用于地图的方法,因为它们没有实现Collection。我该如何实现它们?
  • 你给UserCollectionType的例子只对Hibernate有效。如果必须使用 JPA 特定的 API,这将不起作用(例如,使用 TopLink JPA)。
【解决方案2】:

这是未经测试的。也许您可以尝试看看这是否有效:为什么不直接返回 Map&lt;String, String&gt;

JPA 使用通用集合接口工作:ListSetMap。因此,如果Parameters IS-A Map,您可以将其返回为Map

变化:

public Parameters getParams() {
    return (Parameters) params;
}

收件人:

public Map<String, String> getParams() {
    return params;
}

此外,由于 Hibernate 具有允许延迟加载的类,即 PersistentListPersistentSetPersistentMap 等,您可以使用组合来返回 Hibernate 返回的 Parameters

因此,您基本上可以自定义方法以从返回的 Hibernate 集合中返回值。

这种效果的东西:

public Map<String, String> getParams() {
    return new Parameters(params); //params is injected with Hibernate's PersistentMap.
}

【讨论】:

  • 因为我在其他地方需要 Parameters 类。
  • 如果您的Parameters 类是Map 的子类,您可以将其作为Map 返回。
  • 它是HashMap 的子类(因此实现了Map)。我知道我可以将其返回为 Map,但我不想要 Map
  • 阅读我的回复。 Hibernate 使用反射将一个类型映射到它的通用集合类型。 Parameters 未在 Hibernate 中注册,但仅在 JPA 中注册,因此您必须使用 Map。稍后,在您的代码中,您可以使用instanceof 来检查Map 是否为Parameters
  • 我明白你的意思,但我不想反复检查我是否收到了Parameters 或另一个Map。使用您的最后一种方法,对地图所做的更改不会对模型地图进行,而是对新地图进行。因此,在对地图进行更改后,我还必须再次设置地图。
猜你喜欢
  • 2016-02-20
  • 2013-11-12
  • 1970-01-01
  • 2015-05-26
  • 1970-01-01
  • 2023-03-11
  • 2017-04-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多