在使用TABLE_PER_CLASS导致查询中导致多个表时,如何提高性能?

时间:2016-10-06 11:22:44

标签: java hibernate jpa

我有一个在hibernate中定义的树结构。我有一个名为TreeObject的抽象元素。树对象可以有多个子对象,只有一个父对象。

我也有一些这个类的实现:表单,类别,问题组和问题。所有这些都继承自TreeObject。

这个想法是表单可以作为子类别和问题。该类别可以作为儿童群体和问题,群组可以将孩子作为其他群体和问题。

然后我将TreeObject定义为:

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class TreeObject{

    @ManyToOne
    private TreeObject parent;

    @OneToMany(mappedBy = "parent")
    private List<TreeObject> children;

    [...]
}

然后,其他对象非常简单。例如,表单元素是:

@Entity
public class Form extends TreeObject {
    [...]
}

除了这个问题的一些不相关的代码之外,其他元素是相似的。

当我想要检索元素的子元素时,问题就存在(因为TreeObject不是数据库中的真实表)。例如,要获取表单的所有子项,休眠会从表FormCategoryGroupQuestion创建多重联合以表示TreeObject表等同和选择孩子。当数据库有多个元素(但不是那么多)时,由于生成了多个联合,获取子元素大约需要0.5秒。然后,当我将获得大量数据时,我将在该查询中遇到严重的性能问题。

例如,获取表单获取的查询示例为:

select form0_.ID as ID1_7_0_, form0_.createdBy as createdB3_7_0_,  form0_.name as name2_12_0_, form0_.parent_ID as parent_I5_12_0_, children1_.parent_ID as parent_I5_7_1_, children1_.ID as ID1_12_1_, children1_.ID as ID1_7_2_, children1_.createdBy as createdB3_7_2_, children1_.name as name2_12_2_, children1_.parent_ID as parent_I5_12_2_, children1_.version as version2_2_2_, children1_.clazz_ as clazz_2_, questionva2_.BaseQuestionWithValue_ID as BaseQues1_7_3_, questionva2_.questionValues as question2_38_3_, treeobject3_.ID as ID1_7_4_, treeobject3_.comparationId as comparat2_7_4_, treeobject3_.name as name2_12_4_, treeobject3_.parent_ID as parent_I5_12_4_, treeobject3_.clazz_ as clazz_4_ from form form0_ left outer join 
( select ID, name, parent_ID, 10 as clazz_ from questions
    union select ID, name, parent_ID, 24 as clazz_ from group 
    union select ID, name, parent_ID, 32 as clazz_ from category 
    union select ID, name, parent_ID, 26 as clazz_ from form ) 
children1_ on form0_.ID=children1_.parent_ID left outer join 
    question_with_values questionva2_ on children1_.ID=questionva2_.BaseQuestionWithValue_ID left outer join 
        ( select ID, name, parent_ID, 10 as clazz_ from questions
        union select ID, name, parent_ID, 24 as clazz_ from group 
        union select ID, name, originalReference, parent_ID, 32 as clazz_ fromcategory 
        union select ID, comparationId, name, parent_ID, 26 as clazz_ from form ) 
    treeobject3_ on form0_.parent_ID=treeobject3_.ID where form0_.ID=344820 order by children1_.sortSeq asc;

(注意:我删除了几个列,以便更容易理解代码)

现在我使用@BatchSize来提高性能,应用程序的一般性能更好,但仍然不是真正的解决方案。

我的想法是使用类似@WhereJoinTable的内容来过滤“大”联合查询,只检索类别和问题中的真实子项,而不是所有这些,从而避免了性能问题。但是当child parent映射的子参数时,我不知道如何实现这一点。

    @ManyToOne
    @JoinColumn(name="parent_ID")
    @WhereJoinTable(clause=" ???? ")
    private TreeObject parent; 

可能使用Hibernate的@Filter选项:

    @ManyToOne
    @JoinColumn(name="parent_ID")
    @Filter(name="parentGroup",condition=" ???? ") 
    private TreeObject parent; 

当然,另一种解决方案是将InheritanceType.TABLE_PER_CLASS更改为只有一个大表,因此union不会出现在查询中。但数据库将很难阅读,我想避免它。

问题是:是否有任何方法可以提高Hibernate性能以检索TreeObject的所有子项?

1 个答案:

答案 0 :(得分:1)

ManyToOne注释默认为EAGER,我认为您并不真正需要直接加载的对象的父级(通过主键),对吗?

您可以像这样更改关联:

@ManyToOne(fetch = FetchType.LAZY)
private TreeObject parent;

这至少应该删除与工会的最后一次加入。

但由于模型的性质(它是递归),您将无法在没有本机查询的情况下选择整个对象图,因为JPA根本不支持它。即使是原生查询也可能不是最佳解决方案,我认为您应该考虑使用文档存储,这样您就可以在一次操作中存储/加载整个图形。