此场景在两个方向上使用简单的oneToMany关系与级联持续存在。JPA持续变得越来越慢
很多:
@javax.persistence.Entity(name="Many")
public class Many {
@javax.persistence.ManyToOne(cascade = CascadeType.PERSIST)
protected One one;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long primaryKey;
public void setM(One one) {
this.one = one;
// comment out this line and performance becomes stable
this.one.getMany().add(this);
}
// other setters, getters, etc...
}
一:
@javax.persistence.Entity(name="One")
public class One {
@javax.persistence.OneToMany(mappedBy="m", cascade = CascadeType.PERSIST)
protected java.util.Set<Many> many = com.google.common.collect.Sets.newHashSet();
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long primaryKey;
private String name;
// setters, getters, etc...
}
测试:
public static void main(String[] args) {
while(true) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("test-pu");
EntityManager em = emf.createEntityManager();
for (int i = 0; i < 100; i++) {
sw.reset();
sw.start();
persistMVs(emf, em);
System.err.println("Elapsed: " + sw.elapsed(TimeUnit.MILLISECONDS) + " ms");
}
em.close();
emf.close();
}
}
private static void persistMVs(EntityManagerFactory emf, EntityManager em) {
em.getTransaction().begin();
One one = getOrCreateOne(em);
for (int i = 0; i < 200; i++) {
Many many = new Many();
many.setM(one);
em.persist(many);
}
em.getTransaction().commit();
}
测试是一个无限循环,其尝试插入与单个One
实体关联的20000个Many
个实体。每个循环从创建新的EntityManagerFactory
开始,以显示增加的数据库的负面性能影响。
预期的行为是,实体的插入时间不会急剧增加,但是在每个WHILE CYCLE之后会有一个数量级的增加。
注:
- 我已经试过的EclipseLink,休眠,OpenJPA的和所有从这种放缓的遭遇。
- 如果我不更新One的许多集合,那么就没有降级(请参阅Many的注释行)。
- 如果我没有创建一个新的EntityManagerFactory,那么即使在五十万个实体之后也不会退化。
- 缓慢的部分是
em.persist(many);
(我测量了它)。 - 检出https://github.com/kupsef/OneToMany并使用
gradle start
开始测试。
为什么在这种情况下数据库的初始大小很重要?我应该将此行为视为错误吗?
你为什么不看日志并理解它? –
你会建议什么日志? sql日志仅在第一个周期(内部for)有所不同,它还包含获取Many实体。这并不能解释随后的循环无法获取它们(这很可能是因为它们被缓存以备后用,正如所料)。 – kupsef
您使用的JPA实施的日志。我使用的实现(DataNucleus)总是显示很多信息来跟踪潜在的问题,所以我假定其他人同样有用 –