【问题标题】:How to map custom collection in JPA?如何在 JPA 中映射自定义集合?
【发布时间】:2025-11-29 03:05:01
【问题描述】:

我在使用 JPA(Hiberante 提供程序)映射自定义集合时遇到问题。例如,当我使用具有属性的对象时

List<Match> matches;

<one-to-many name="matches">
    <cascade>
        <cascade-all />
    </cascade>
</one-to-many>

在我的ORM文件中,没关系;但是如果我将 "List matches;" 替换为

private Matches matches;

,其中 "Matches" 定义如下:

public class Matches extends ArrayList<Match> {

    private static final long serialVersionUID = 1L;
}

它会产生以下错误:

Caused by: org.hibernate.AnnotationException: Illegal attempt to map a non collection as a @OneToMany, @ManyToMany or @CollectionOfElements: by.sokol.labs.jpa.MatchBox.matches

感谢您的关注!

【问题讨论】:

    标签: java hibernate collections jpa persistence


    【解决方案1】:

    您可以,但您必须将其称为常见集合之一 - ListSet

    所以:

    private List matches = new Matches();
    

    为什么?例如,因为 Hibernate 会代理您的集合以启用延迟加载。因此它创建了PersistentListPersistentSetPersistentBag,它们是List,但不是Matches。因此,如果您想向该集合添加其他方法 - 好吧,您不能。

    Check this article了解更多详情。

    不过,您有一个解决方案。不要使用继承,使用组合。例如,您可以向您的实体添加一个名为 getMatchesCollection() 的方法(除了传统的 getter),如下所示:

     public Matches getMatchesCollection() {
        return new Matches(matches);
     }
    

    你的Matches 类看起来像(使用google-collections'ForwardingList):

    public class Matches extends ForwardingList {
        private List<Match> matches;
        public Matches(List<Match> matches) { this.matches = matches; }
        public List<Match> delegate() { return matches; }
        // define your additional methods
    }
    

    如果你不能使用google集合,只需自己定义ForwardingList - 它调用底层List的所有方法

    如果您不需要任何额外的方法来对结构进行操作,则不要定义自定义集合。

    【讨论】:

      【解决方案2】:

      Hibernate 需要将持久的集合值字段声明为接口类型(因为它们将被 Hibernate 的实现替换以实现延迟加载)。来自参考文档:

      6.1. Persistent collections

      Hibernate 要求将持久的集合值字段声明为接口类型。例如:

      public class Product {
          private String serialNumber;
          private Set parts = new HashSet();
      
          public Set getParts() { return parts; }
          void setParts(Set parts) { this.parts = parts; }
          public String getSerialNumber() { return serialNumber; }
          void setSerialNumber(String sn) { serialNumber = sn; }
      }
      

      实际的界面可能是 java.util.Set, java.util.Collection, java.util.List, java.util.Map, java.util.SortedSet, java.util.SortedMap 或任何你喜欢的东西 喜欢(“你喜欢的任何东西”是指你 将不得不编写一个实现 的 org.hibernate.usertype.UserCollectionType.)

      注意实例变量是如何 用一个实例初始化 HashSet。这是最好的方法 初始化集合值 新实例化的属性 (非持久)实例。当你 使实例持久化,通过 例如,调用persist(), Hibernate 实际上将取代 HashSet 有一个实例 Hibernate 自己实现的Set

      所以你的第二种方法是不可能的,至少不是你声明的方式。但老实说,我真的不明白这一点。

      【讨论】: