2014-07-03 209 views
6

使用JPA 1(hibernate-core版本3.3.0.SP1和hibernate-entitymanager版本3.4.0.GA): 我有一些類似於下面定義的實體,其中ChildOne和ChildTwo從父親實體。JPA繼承問題

@Entity 
@Table(name = "TABLE_FATHER") 
@Inheritance(strategy = InheritanceType.JOINED) 
@DiscriminatorColumn(discriminatorType = DiscriminatorType.INTEGER, name = Father.C_ID_CTG) 
public class Father { 

@Id 
@GeneratedValue(strategy = GenerationType.AUTO, generator = "sq") 
@Column(name = "ID_PK", nullable = false) 
@BusinessId 
private Long id; 
... 
} 

@Entity 
@Table(name = "TABLE_CHILD_ONE") 
@Inheritance(strategy = InheritanceType.JOINED) 
@DiscriminatorValue(Categories.ID_CTG_ONE) 
public class ChildOne extends Father { 
    ... 
} 

@Entity 
@Table(name = "TABLE_CHILD_TWO") 
@Inheritance(strategy = InheritanceType.JOINED) 
@DiscriminatorValue(Categories.ID_CTG_TWO) 
public class ChildTwo extends Element { 
    ... 
} 

比方說,我有一個實體具有父元素,另一個具有父元素的集合。在這兩種情況下,都應該去兒童實體。

@Entity 
@Table(name = "TABLE_ONE") 
public class OneTable { 

@JoinColumn(name = "ID_PK", referencedColumnName = "ID_PK", nullable = false) 
@ManyToOne(optional = false, fetch = FetchType.LAZY) 
private Father element; 
    ... 
} 

@Entity 
@Table(name = "TABLE_ANOTHER") 
public class Another { 

@Fetch(FetchMode.JOIN) 
@OneToMany(cascade = CascadeType.ALL, mappedBy = "id", fetch = FetchType.LAZY) 
private Collection<Father> elementCollection; 

    ... 
} 

我期待獲得永遠的子元素,但是當我得到getElement()返回父元素 和,在另一方面,當我得到getElementCollection()孩子元素來集合的元素。

顯然,@JoinColumn是造成這種行爲的原因,與父表進行連接並忘記子元素。 集合按預期工作。

我怎麼能得到getElement()電話的兒童元素?任何想法或workarround? 在此先感謝。

回答

2

問題不是由@JoinColumn造成的。 原因是延遲加載。 我設法在更簡單的例子中查明您的問題。 原諒我改變從父親到父母的習俗。

在下面的示例中,未初始化的元素類型爲jpa.inheritance.issue.Parent_$$_javassist_1。它是一個Hibernate代理 - 動態創建的Parent的子類。 您可以通過調用Hibernate專有的API getHibernateLazyInitializer().getImplementation()來「解除」它。

elementCollection的集合也是Lazy Initialized。收集的類型是org.hibernate.collection.PersistentBag,在第一次訪問時正在使用正確的數據進行初始化。 集合一次初始化。 請參閱您的確切版本的Hibernate(3.3.0.SP1/3.4.0.GA)成功通過綠色測試。

@Test 
    public void test() { 
     Child c = new Child(); 
     em.persist(c); 

     Another a = new Another(); 
     a.setElement(c); 
     Collection<Parent> col = new ArrayList<Parent>(); 
     col.add(c); 
     a.setElementCollection(col); 
     em.persist(a); 
     c.setAnother(a); 

     long idx = a.getId(); 
     tx.commit(); 

     // I'm cleaning the cache to be sure that call to a.getElement() will return proxy. 
     em.clear(); 
     tx = em.getTransaction(); 
     tx.begin(); 

     a = em.find(Another.class, idx); 
     Assert.assertNotNull(a); 
     Parent p = a.getElement(); 
     // At this point p is a type of jpa.inheritance.issue.Parent_$$_javassist_1 

     Assert.assertTrue(p instanceof Parent); 
     Assert.assertFalse(p instanceof Child); 

     // At this point a.elements is a not initialized (empty) collection of type org.hibernate.collection.PersistentBag 
     // When we access this collection for the first time, records are read from the database 
     Assert.assertEquals(1, a.getElementCollection().size()); 

     if (p instanceof HibernateProxy) { 
      p = 
        (Parent) ((HibernateProxy) p).getHibernateLazyInitializer() 
          .getImplementation(); 
     } 

     // At this point p is a type of jpa.inheritance.issue.Child 
     Assert.assertTrue(p instanceof Child); 
    } 

    @Entity 
    public class Another { 

     @JoinColumn(name = "element_id", referencedColumnName = "id", nullable = false) 
     @ManyToOne(fetch=FetchType.LAZY) 
     private Parent element; 
     public Parent getElement() { 
      return element; 
     } 

     public void setElement(Parent element) { 
      this.element = element; 
     } 

     @OneToMany(cascade = CascadeType.ALL, mappedBy = "another", fetch = FetchType.LAZY) 
     public Collection<Parent> elements; 

     public Collection<Parent> getElementCollection() { 
      return elements; 
     } 

     public void setElementCollection(Collection<Parent> elementCollection) { 
      this.elements = elementCollection; 
     } 

     // @Id ... 
    } 

    @Entity 
    @Inheritance(strategy = InheritanceType.JOINED) 
    public class Parent { 
     @ManyToOne 
     private Another another; 

     public Another getAnother() { 
      return another; 
     } 

     public void setAnother(Another another) { 
      this.another = another; 
     } 

     // @Id ... 
    } 

    @Entity 
    public class Child extends Parent {   
    } 

,因爲這些註釋都需要與InheritanceType.SINGLE_TABLE作爲唯一的手段,以確定類型,你不必@DiscriminatorColumn也不@DiscriminatorValue。 通過InheritanceType.JOINED Hibernate能夠通過檢查兩個(Parent和Child)表中是否有記錄具有相同的Id來確定多態類型。 您可以打開hibernate日誌記錄來查看查詢如何確定類型。它的工作原理是這樣的:

select 
    another0_.id as id0_1_, 
    another0_.element_id as element2_0_1_, 
    parent1_.id as id1_0_, 
    parent1_1_.name as name2_0_, 
    case 
     when parent1_1_.id is not null then 1 
     when parent1_.id is not null then 0 
     else -1 
    end as clazz_0_ 
from 
    Another another0_ 
inner join 
    Parent parent1_ 
     on another0_.element_id=parent1_.id 
left outer join 
    Child parent1_1_ 
     on parent1_.id=parent1_1_.id 
where 
    another0_.id=? 
+0

好點。讓我嘗試一些事情,我會讓你知道... – elcadro

+0

只是關於你的答案的幾個筆記...據我所知,規範不需要實現使用鑑別器列來實現JOINED繼承,但是,假設是,如果@DiscriminatorColumn被指定,那麼它將被用於... 另一方面,儘管你的解決方案實際上工作,仍然有一些疑問。知道了懶惰返回代理,爲什麼ManyToOne返回代理到父(jpa.inheritance.issue.Parent _ $$ _ javassist_1)和OneToMany集合子代理(jpa.inheritance.issue.ChildOne _ $$ _ javassist_1)? – elcadro

+0

實際應用中getElement()調用的確切返回類型不確定。如果在第一級或第二級緩存中找到該對象,將會鍵入「Child」。這就是爲什麼我在中間調用'em.clean()'來查明特定行爲的原因。這就是爲什麼在父子層次結構的情況下最好是類型不可知的。如果你真的需要一個正確的類型,你可以像我提議的那樣「取消代理」,或者更好地將FetchType改爲EAGER(因爲無論如何都是「unproxying」加載關係)。 – zbig