0

在尝试使用注释设置我的模式时,我遇到了一些令人沮丧的 Hibernate 行为。假设我有一个与其他几种类型具有一对多关系的实体。例如:

@Entity
@Table(name = "clubs")
public class Club {
    private long id;
    private String name;
    //...

    private Collection<ClubLocation> locations;

    public Club() {
        this.name = null;
        //...

        this.locations = Collections.emptyList();
    }

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }

    @Column(nullable = false)
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    @OneToMany(mappedBy = "club")
    public Collection<ClubLocation> getLocations() {
        return locations;
    }
    public void setLocations(Collection<ClubLocation> locations) {
        this.locations = locations;
    }
}

现在正如我提到的,我想将此实体与许多其他实体相关联,因此定义一个管理关系的超类实体似乎是个好主意,Club这样我就不需要在每个关联的实体中设置关系班级。我这样做使用:

@Entity
@Table(name = "clubItems")
@Inheritance(strategy = InheritanceType.JOINED)
public class ClubItem {
    protected Club club;

    public ClubItem() {
        this.club = null;
    }

    @ManyToOne(optional = false)
    public Club getClub() {
        return club;
    }
    public void setClub(Club club) {
        this.club = club;
    }
}

然后我像这样扩展该类:

@Entity
@Table(name = "clubLocations")
public class ClubLocation extends ClubItem {
    private String title;
    //...

    public ClubLocation() {
        this.title = null;
        //...
    }

    @Column(nullable = false)
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
}

一切似乎都很好,除了当我尝试运行然后 Hibernate 爆炸时:

org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: ClubLocation.club in Club.locations
[WARNING] [talledLocalContainer]    at org.hibernate.cfg.annotations.CollectionBinder.bindStarToManySecondPass(CollectionBinder.java:543)
[WARNING] [talledLocalContainer]    at org.hibernate.cfg.annotations.CollectionBinder$1.secondPass(CollectionBinder.java:508)
[WARNING] [talledLocalContainer]    at org.hibernate.cfg.CollectionSecondPass.doSecondPass(CollectionSecondPass.java:43)
[WARNING] [talledLocalContainer]    at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1127)
[WARNING] [talledLocalContainer]    at org.hibernate.cfg.AnnotationConfiguration.secondPassCompile(AnnotationConfiguration.java:296)
[WARNING] [talledLocalContainer]    at org.hibernate.cfg.Configuration.buildMappings(Configuration.java:1112)
[WARNING] [talledLocalContainer]    at org.hibernate.ejb.Ejb3Configuration.buildMappings(Ejb3Configuration.java:1233)
[WARNING] [talledLocalContainer]    at org.hibernate.ejb.EventListenerConfigurator.configure(EventListenerConfigurator.java:154)
[WARNING] [talledLocalContainer]    at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:869)
[WARNING] [talledLocalContainer]    at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:183)
[WARNING] [talledLocalContainer]    at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:240)
[WARNING] [talledLocalContainer]    at org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:120)
[WARNING] [talledLocalContainer]    at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:83)
[WARNING] [talledLocalContainer]    at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:60)

令人沮丧的是,如果我复制类club内部属性的 getter 和 setter 声明ClubLocation,一切都很好。具体来说,如果我添加它可以找到:

@Override
@ManyToOne(optional = false)
public Club getClub() {
    return super.getClub();
}
public void setClub(Club club) {
    super.setClub(club);
}

...进入ClubLocation课堂。但这毫无意义,因为它所做的只是委托给超类。

Hibernate 是否不够复杂,无法在没有在子类中重新声明 getter/setter 的情况下处理继承的映射字段?如果 Hibernate 可以更优雅地处理这种情况,它需要如何设置/我做错了什么?

4

2 回答 2

1

我在一个项目中有类似的设置。我认为解决这个问题的一种简单且或多或少的好方法是改变

private Collection<ClubLocation> locations;

进入

private Collection<ClubItem> items;

(以及相关的 getter、setter)。这样它肯定有效。

如果您出于方便的原因需要在 Club 中使用 getLocations 方法,您可以自由创建它。如果您确定这些项目将仅包含位置,则可以按原样返回集合,否则您需要在 Java 代码中对其进行过滤。

一个缺点是,如果项目还包含非位置元素,Java 中的过滤会占用内存和 CPU,因此可能会很慢。

于 2012-07-19T06:24:11.640 回答
1

对于 JPA,您的映射是完全有效的。也例如在 EclipseLink 中,它按原样工作。这是 Hibernate 实现中的问题。

这篇博文提供了一种解决方法。不幸的是,所有这些都不完美:表结构的变化,丢失类型以使用映射的超类进行查询等。

于 2012-07-19T06:39:36.160 回答