#Stable Diffusion 美图活动一期#
关于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条数据的时候,接口调用跟数据持久化耗时各占一半,这结果还是比较可以接受的。



















