关于MyBatis与JPA:
笔者初次接触这两个持久层框架的时候,那还是得从iBatis、Hibernate开始说起。那时候知道的一个很浅显、但最明显的区别就是:iBatis是半自动化的ORM框架,适用于表关联关系复杂的项目;Hibernate是全自动化的ORM框架,适用于表关联关系简单的项目。
个人还是比较喜欢用MyBatis的,那种你可以随意掌控SQL的自由是无法溢于言表的,虽然要时刻提心吊胆的考虑数据库兼容性。
业务背景:
最近在处理一个从第三方接口获取数据并持久化的业务,当数据量达到3000条左右的时候,整个过程耗时将近130s,离大谱。
问题分析及定位:
一开始本着专业甩锅的精神,怀疑是第三方接口比较拉跨,我们也确实有不具任何说明力的证据,即数据量小的时候,接口调用耗时占了大头。
但甩锅归甩锅,自查还是要进行的。数据量级过百之后,耗时大头就不再是接口调用了,经过多次调试最终定位了问题所在行:saveAndFlush。
抢救手段:
1、将一些基础查询的结果缓存在内存中;
2、根据业务字段对接口结果集进行去重;
3、调整代码结构,将saveAndFlush改为saveAllAndFlush;
4、配置批量操作参数;
spring:
jpa:
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQL9Dialect
hbm2ddl:
auto: none
format_sql: true
jdbc:
batch_size: 1000
fetch_size: 1000
batch_versioned_data: true
order_inserts: true
order_update: true
结果:
收效甚微,耗时没有任何质的提升。
好奇:
这个批量操作到底做甚了???
@Transactional
public <S extends T> List<S> saveAll(Iterable<S> entities) {
Assert.notNull(entities, "Entities must not be null!");
List<S> result = new ArrayList();
Iterator var3 = entities.iterator();
while(var3.hasNext()) {
S entity = var3.next();
result.add(this.save(entity));
}
return result;
}
继续好奇:
那这个 saveAndFlush又干的啥?
@Transactional
public <S extends T> S saveAndFlush(S entity) {
S result = this.save(entity);
this.flush();
return result;
}
GO ON
@Transactional
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null.");
if (this.entityInformation.isNew(entity)) {
this.em.persist(entity);
return entity;
} else {
return this.em.merge(entity);
}
}
但凡用过hibernate或jpa的看到这都能明白,而且这个保存或更新本身也是“全自动”框架提供的一个特点。
结论:
既然目前使用JPA在框架层面没有特别好的解决办法,我们能不能人工干预一下呢?将接口返回的结果集,按业务进行人工分离,需要保存的一伙,需要更新的一伙,然后直接使用EntityManager的persist和merge。
实践验证:
使用EntityManager的persist和merge之后,总耗时控制在15s左右,效率提升了8-9倍,比较可观,3000条数据的时候,接口调用跟数据持久化耗时各占一半,这结果还是比较可以接受的。