是否可以映射(JPA)在抽象超类中使用泛型的Java类型层次结构?

时间:2014-02-25 16:37:32

标签: java jpa orm

我一直在阅读很多关于JPA映射的SO问题和博客文章和文档,但似乎没有什么能清楚地解决我的情况(这让我感到惊讶,因为我确信我不是第一个想要映射的人像这样的类层次结构。)

我有一个很好的,设计干净的对象模型,我需要通过JPA进行映射。它如下

public abstract class BaseEntity<T extends BaseEntity> {
    private Integer id;
    private List<T> children;
}

public class ConcreteEntity1 extends BaseEntity<ConcreteEntity2> {
    private String value;
}

public class ConcreteEntity2 extends BaseEntity{
    private String foo;
}

我似乎无法将其映射到地图上。我得到的最接近的是这样的(我现在正在使用XML映射,虽然我也尝试过注释):

<mapped-superclass class="com.me.datamodel.BaseEntity" access="FIELD">
    <attributes>
        <id name="id">
            <column name="auto_id" nullable="false"/>
            <generated-value strategy="IDENTITY"/>
        </id>
    </attributes>
</mapped-superclass>

<entity class="com.me.datamodel.ConcreteEntity1" access="FIELD">
    <attributes>
        <basic name="value" />
        <one-to-many name="children" fetch="EAGER" mapped-by="media">
            <cascade><cascade-all /></cascade>
        </one-to-many>
    </attributes>
</entity>

<entity class="com.me.datamodel.ConcreteEntity2" access="FIELD">
    <attributes>
        <basic name="foo" />
    </attributes>
</entity>

问题在于JPA抱怨childrenConcreteEntity1的映射。它声称children(BaseEntity)的目标实体不是实体。嗯,这是正确的,'BaseEntity'不是一个实体。但children的实际类型不是 BaseEntity,而是ConcreteEntity2(由ConcreteEntity1的类型参数声明)。

那么如何映射这样的对象模型呢?我已经尝试了很多变化,但都没有工作(它们都在实体管理器初始化期间失败)。

1 个答案:

答案 0 :(得分:2)

我认为你几乎所有内容都是正确的,除了在BaseEntity中正确延伸ConcreteEntity2:你忘了说类型参数如public class ConcreteEntity2 extends BaseEntity<ConcreteEntity2> {...}。因此,它无法映射ConcreteEntity2,因此无法映射ConcreteEntity1

所以我的工作代码(使用EclipseLink 2.5.1和Hibernate 4.3.1.Final测试):

@MappedSuperclass
public abstract class BaseEntity<T extends BaseEntity> {
    private Integer id;
    private List<T> children;

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

    @OneToMany(cascade=CascadeType.ALL)
    public List<T> getChildren() {
        return children;
    }
    public void setChildren(List<T> children) {
        this.children = children;
    }
}

@Entity
public class ConcreteEntity1 extends BaseEntity<ConcreteEntity2> {
    private String value;

    public String getValue() {
        return value;
    }
    public void setValue(String value) {
        this.value = value;
    }
}
@Entity
public class ConcreteEntity2 extends BaseEntity<ConcreteEntity2> {
    private String foo;

    public String getFoo() {
        return foo;
    }
    public void setFoo(String foo) {
        this.foo = foo;
    }
}

JPA尝试映射的每个实体中的一些重要说明,如果持久字段是集合,那么它必须是实体类型的集合(JPA规范章节 2.2持久字段和属性)。如果你不知道ConcreteEntity2的孩子究竟是什么(除了他们是BaseEntity),你必须要么BaseEntity一个实体,要么引入另一个实体来扩展子类并改为使用它。在上述任何一种情况下,您都必须使用JPA继承。