2014-02-25 86 views
3

我一直在閱讀很多有關JPA映射的SO問題和博客文章和文檔,但似乎沒有清楚地解決我的情況(這讓我感到驚訝,因爲我確信我不是第一個想要映射像這樣的類層次結構)。是否可以映射(JPA)在抽象超類中使用泛型的Java類型層次結構?

我有一個很好的,乾淨設計的對象模型,我需要通過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的類型參數所聲明的)。

那麼如何映射這樣的對象模型呢?我已經嘗試了很多變體,但都沒有工作(它們在實體管理器的初始化過程中都失敗了)。

+0

對不起,這是一個錯字。我修正了代碼以正確顯示'ConreteEntity1'和'ConcreteEntity2'都擴展了'BaseEntity'。 –

+1

一旦我分配了它,我無法撤消,因此我只能等到所有集成測試都顯示此映射沒有創建任何問題。 –

回答

2

我想你幾乎一切正確,除了正確延伸BaseEntityConcreteEntity2:你忘了說類型參數像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繼承。

+0

謝謝。看起來關鍵是不得不在每個是@Entity的子類上指定類型參數。目前還不清楚爲什麼JPA/Hibernate要求(顯然Java不),但我可以忍受它。 –

+0

對此最大的失望是,我得到的錯誤沒有提供如何解決這個問題。他們指出了所有不同的方向(取決於我嘗試過的各種東西),但從來沒有在沒有類型參數的具體類中。 –

+0

我可以這樣解釋:我在回答中提到每個持久性集合都必須只包含實體。而且由於擴展時沒有指定類型,所以每個Java代碼實際上都會看到該列表的類型爲'List ',但BaseEntity不是實體。 –

相關問題