JPA继承问题

时间:2014-07-03 11:19:58

标签: java hibernate inheritance jpa

使用JPA 1(hibernate-core版本3.3.0.SP1和hibernate-entitymanager版本3.4.0.GA): 我有一些类似于下面定义的实体,其中ChildOne和ChildTwo从父实体扩展。

@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? 提前谢谢。

1 个答案:

答案 0 :(得分:2)

问题不是由@JoinColumn引起的。 原因是延迟加载。 我设法在更简单的例子中找出你的问题。 请原谅我将父亲的约定改为父母。

在下面的示例中,未初始化的元素是jpa.inheritance.issue.Parent_$$_javassist_1的类型。它是一个Hibernate代理 - 动态创建的Parent子类。 你可以&#34; unproxy&#34;它通过调用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 {         
    }

您不需要@DiscriminatorColumn@DiscriminatorValue因为InheritanceType.SINGLE_TABLE需要这些注释作为确定类型的唯一手段。 使用InheritanceType.JOINED Hibernate能够通过检查具有相同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=?