双向变得慢于单向

时间:2017-07-21 16:38:56

标签: java performance hibernate bidirectional

在Java / Hibernate应用程序中,我有两个类CatKitten的双向关系,如下图所示:

public class Cat {
  ...
  @OneToMany(mappedBy="cat", fetch = FetchType.LAZY)
  @OnDelete(action = OnDeleteAction.CASCADE)
  @LazyCollection(LazyCollectionOption.EXTRA)
  @Getter
  @Setter
  private List<Kitten> kittens = new LinkedList();
  public void addKitten(Kitten k) {
    kittens.add(k);
  }
  ...
}  

public class Kitten {
  ...   
  @ManyToOne(fetch=FetchType.LAZY)
  @Getter
  @Setter
  private Cat cat;
  ...
}

在一个巨大的for循环中,20000 Kitten被添加到之前创建的不同Cat实体中。 for循环中的重要代码如下所示:

....
Kitten k = new Kitten();
k.setAttribut("foo");
k.setCat(currentCat);     // (a) line
currentCat.addKitten(k);  // (b) line
daoFactory.getKittenDao().save(k);
...

代码正在运行,但不知何故,性能非常慢。在之前的迭代(单向)中,应用程序要快得多。由于最终版本应该在1000000 Kitten上运行,因此必须有一种改进的方法。如果我对上面代码的时间进行基准测试,则大约需要40秒。如果我通过删除线(a)或(b)来模拟单向,则在两种情况下都需要10秒(但是如果我访问对象则会稍后出现)。

所以我的问题是: 我是否想念一些东西,或者Hibernate中双向关系的内部维护是否很慢?由于模拟单向更快,我预计双向运行时间约为15秒。

更新

保存实体的代码位于SAX-Parser DefaultHandler实现中。因此,在解析XML结构时,首先Cat保存在startElement()函数中,稍后Kitten将保存在另一个startElement()调用中。 关于@Bartun的建议/问题:我看了一下批处理。问题是,通过使用DAO和startElement()函数,很难说,何时保存了50个实体来刷新会话。计数器可以做到这一点。但是,它没有解释通过建立双向性对性能的影响。 由于事务管理Spring @Transactional用于启动XML解析的函数。

2 个答案:

答案 0 :(得分:1)

我已将处理时间缩短到我可以忍受的值。它并没有真正解决我的问题,但显着减少了时间。如果其他人有同样的问题,这里是当前的代码和一个简短的解释。

public class Cat {
  ...
  @OneToMany(mappedBy="cat", fetch = FetchType.LAZY)
  @OnDelete(action = OnDeleteAction.CASCADE)
  @LazyCollection(LazyCollectionOption.EXTRA)
  @Getter
  @Setter
  private Set<Kitten> kittens = new HashSet();
  public void addKitten(Kitten k) {
     k.setCat(this);
     if (Hibernate.isInitialized(kittens)) kittens.add(k); //line X
  }
  ...
}  

public class Kitten {
  ...   
  @ManyToOne(fetch=FetchType.LAZY)
  @OnDelete(action = OnDeleteAction.CASCADE)
  @Getter
  @Setter
  private Cat cat;
  ...
}
  • 最重要的是line X。在原始代码中,每次添加一只小猫时,都会从数据库中加载整个小猫列表。当我找到here时,如果列表尚未被初始化,则不得添加小猫。我知道,这必须小心,因为每个小猫必须在首次初始化列表之前保存在数据库中。否则一切都会失去同步。
  • 上面的代码中没有插图,我改变了小猫持久性的结构以启用批量插入(Thx Bartun用于链接)。现在,所有小猫都在解析过程结束时使用批处理保存,而不是单独保存每只小猫。
  • ListSet的变化也是一个很小的改进,以便稍后在代码中启用多个fetch joins

答案 1 :(得分:0)

你在哪里开业?确保您使用的是一笔交易

此外,你应该使用批量查询这样的金额

有关批量https://vladmihalcea.com/how-to-batch-insert-and-update-statements-with-hibernate/

的详细信息

您还可以使用hibernate statictics检查刷新计数和其他事项 https://www.thoughts-on-java.org/how-to-activate-hibernate-statistics-to-analyze-performance-issues/

当您处理如此数量的内容时,您需要完全控制刷新/批处理

相关问题