如何为一对多关联加速Hibernate DML语句

时间:2015-05-07 04:41:49

标签: java performance hibernate jpa orm

我一直在尝试优化hibernate应用程序,而我遇到的Hibernates效率最大的问题之一是它倾向于使用子实体执行n + 1查询以进行简单的crud操作。我已经能够通过在子实体(多对一端)上使用@Fetch(FetchMode.JOIN)来阻止对选择操作的n + 1查询,但这不会影响更新/插入/删除查询。以下是相关实体和属性的示例:

// parent entity for Mean and Covariance entities
@Entity
@DynamicInsert
@Table(name = "belief")
public class Belief implements Serializable, Cloneable {

    // surrogate key
    @Id
    @GeneratedValue
    @Column(name = "belief_id", unique = true, insertable = false, updatable = false)
    private Integer id;

    // other properties...

    @Cascade(org.hibernate.annotations.CascadeType.ALL)
    @OneToMany(mappedBy = "pk.beliefId", orphanRemoval = true, fetch = FetchType.LAZY)
    private List<Mean> means = new ArrayList<>();

    @Cascade(org.hibernate.annotations.CascadeType.ALL)
    @OneToMany(mappedBy = "pk.beliefId", orphanRemoval = true, fetch = FetchType.LAZY)
    private List<Covariance> covariances = new ArrayList<>();

}

@Entity
@DynamicInsert
@Table(name = "mean")
public class Mean implements Serializable, Cloneable {

    // composite key
    @EmbeddedId
    private MeanPK pk = new MeanPK(this);

    @Fetch(FetchMode.JOIN)
    @ManyToOne(fetch = FetchType.LAZY, cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH })
    @JoinColumn(name = "belief_id", insertable = false, nullable = false, updatable = false)
    private Belief belief;

    // other properties...
}

@Entity
@DynamicInsert
@Table(name = "covariance")
public class Covariance implements Serializable, Cloneable {

    // composite key
    @EmbeddedId
    private CovariancePK pk = new CovariancePK(this);

    @Fetch(FetchMode.JOIN)
    @ManyToOne(fetch = FetchType.LAZY, cascade = { CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH })
    @JoinColumn(name = "belief_id", insertable = false, nullable = false, updatable = false)
    private Belief belief;

    // other properties...
}

因此,当我执行session.delete(belief);时,日志显示为每个协方差执行单独的删除语句,并且表示信念实体可能已引用。以下是日志示例:

Hibernate: select belief0_.belief_id as belief_i1_0_0_, belief0_.after_comb as after_co2_0_0_, belief0_.description as descript3_0_0_, belief0_.name as name4_0_0_, belief0_.project_id as project_7_0_0_, belief0_.type as type5_0_0_, belief0_.version as version6_0_0_ from belief belief0_ where belief0_.belief_id=?
Hibernate: select covariance0_.belief_id as belief_i1_0_0_, covariance0_.belief_id as belief_i1_1_0_, covariance0_.col_variable_id as col_vari2_1_0_, covariance0_.row_variable_id as row_vari3_1_0_, covariance0_.belief_id as belief_i1_1_1_, covariance0_.col_variable_id as col_vari2_1_1_, covariance0_.row_variable_id as row_vari3_1_1_, covariance0_.variance as variance4_1_1_, covariance0_.version as version5_1_1_, variable1_.variable_id as variable1_5_2_, variable1_.definition as definiti2_5_2_, variable1_.description as descript3_5_2_, variable1_.name as name4_5_2_, variable1_.project_id as project_6_5_2_, variable1_.version as version5_5_2_, variable2_.variable_id as variable1_5_3_, variable2_.definition as definiti2_5_3_, variable2_.description as descript3_5_3_, variable2_.name as name4_5_3_, variable2_.project_id as project_6_5_3_, variable2_.version as version5_5_3_ from covariance covariance0_ inner join variable variable1_ on covariance0_.col_variable_id=variable1_.variable_id inner join variable variable2_ on covariance0_.row_variable_id=variable2_.variable_id where covariance0_.belief_id=?
Hibernate: select means0_.belief_id as belief_i1_0_0_, means0_.belief_id as belief_i1_2_0_, means0_.variable_id as variable2_2_0_, means0_.belief_id as belief_i1_2_1_, means0_.variable_id as variable2_2_1_, means0_.mean as mean3_2_1_, means0_.swept as swept4_2_1_, means0_.version as version5_2_1_, variable1_.variable_id as variable1_5_2_, variable1_.definition as definiti2_5_2_, variable1_.description as descript3_5_2_, variable1_.name as name4_5_2_, variable1_.project_id as project_6_5_2_, variable1_.version as version5_5_2_ from mean means0_ inner join variable variable1_ on means0_.variable_id=variable1_.variable_id where means0_.belief_id=?
Hibernate: delete from covariance where belief_id=? and col_variable_id=? and row_variable_id=? and version=?
Hibernate: delete from covariance where belief_id=? and col_variable_id=? and row_variable_id=? and version=?
Hibernate: delete from mean where belief_id=? and variable_id=? and version=?
Hibernate: delete from mean where belief_id=? and variable_id=? and version=?
Hibernate: delete from mean where belief_id=? and variable_id=? and version=?
Hibernate: delete from mean where belief_id=? and variable_id=? and version=?
Hibernate: delete from mean where belief_id=? and variable_id=? and version=?
Hibernate: delete from belief where belief_id=? and version=?

我已经为这个n + 1问题做了大量谷歌搜索,我只找到了选择操作的解决方案,而不是级联插入/更新/删除操作。 有没有人知道如何修复和优化? 谢谢!

2 个答案:

答案 0 :(得分:1)

Hibernate将entity state transitions转换为DML语句,这就是为每个被删除的实体都有一个DELETE语句的原因。

shouldn't use EAGER fetching,因为基于查询的提取策略总是更灵活,并且会产生最佳性能。关联应该是LAZY,您应该使用JOIN FETCH指令来检索每个特定业务案例所需的关系。

您还应该为:

启用语句批处理

如果您有写密集型应用程序,则可以使用批量UPDATE/DELETE HQL/JPQL支持使用批量语句。

答案 1 :(得分:0)

你基本上处于预期的Hibernate魔法已经结束了一段时间,并以OR-Mapping的现实结束。

您可以从OneToMany中删除FetchType.Lazy,因为这是默认值。 FetchType.Eager将是克服select语句的n + 1问题的方法。你也没有这样做(或者说你混合hibernate和jpa的东西不确定在那种情况下赢了什么)。问题为什么要进行批量删除?如果是,那么这样做,但不要指望hibernate为你做出决定。请记住,通过一组id删除可能不会触发级联和孤立删除。 如果您期望该列表中的大量条目比重构映射的时间要多。

!!!但最重要的是!!!

您的映射中有多个内容,这些内容似乎具有更高的负面性能影响,并且在某些情况下会导致意外行为。在我看来,你想要性能优化某个不是最大问题的东西。只要您没有删除性能问题,请不要做任何事情。

  • 混合两个获取系统
  • 高级联使用 - 为何做出此决定。他们真的是一个思想过程吗?或者为您解决了一个问题,可以通过手动处理它们以更清洁的方式解决。
  • 是否有一个特定的原因导致EmbeddedId的东西无法通过简单的Long ID处理?对我来说似乎有点过头了。
  • 我不怀疑JoinColumn可插入等东西。我只是希望背后有一个原因。