【问题标题】:Getting better performance with JpaRepository.saveAll()使用 JpaRepository.saveAll() 获得更好的性能
【发布时间】:2021-02-05 06:01:05
【问题描述】:

我正在使用一个 rest-api,它应该从 csv 文件中导入数据。上传和映射到对象部分正在工作,但不是 saveAll(),将 130000~ 行保存到数据库(在 mssqlserver 上运行)只需要数年时间,它应该可以在更短的时间内处理更大的文件。

这是我的数据类的样子:

@Entity
data class Street(
        @Id
        @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "streetSeq")
        @SequenceGenerator(name = "streetSeq", sequenceName = "streetSeq", allocationSize = 1)
        val id: Int,
        val name: String?,
        val municipalityId: Int?
)

我只是使用 saveAll 方法。导入方法中的所有内容都相对快速(例如 10 秒),直到 saveAll()

override fun import(file: MultipartFile) {

    val inputStream = file.inputStream
    var import: List<Street> = listOf()
    tsvReader.open(inputStream) {
        val csvContents = readAllWithHeaderAsSequence()
        val dataClasses = grass<ImportStreet>().harvest(csvContents)

        dataClasses.forEach { row ->
            import = import + toStreet(row)
        }
        println("Data Converted")
    }
    streetRepository.saveAll(import)
    inputStream.close()
}

我已经尝试过调整 application.yml,但差别不大。

   jpa:
    properties:
      hibernate:
        ddl-auto: update
        dialect: org.hibernate.dialect.SQLServer2012Dialect
        generate_statistics: true
        order_inserts: true
        order_updates: true
        jdbc:
          batch_size: 1000

【问题讨论】:

  • 第一次尝试,在实体上添加@Batch。如果它不起作用,请查看Spring-Batch

标签: sql-server hibernate kotlin jdbc


【解决方案1】:

正如您在 SimpleJpaRepository 的实现中看到的那样,它没有进行任何类型的批处理保存,它只是一个一个地保存每个实体,这就是它缓慢的原因 https://github.com/spring-projects/spring-data-jpa/blob/d35ee1a82bf0fdf2de2724a02619eea1cf3c98bd/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java#L584

    Assert.notNull(entities, "Entities must not be null!");

    List<S> result = new ArrayList<S>();

    for (S entity : entities) {
        result.add(save(entity));
    }

    return result;

所以尝试在不使用spring-data-jpa的情况下实现批量保存,例如使用spring-batch

【讨论】:

    【解决方案2】:

    正如其他 cmets / awnsers 建议的那样,我尝试使用 spring-batch,但它对我没有用(主要是因为我不知道如何使用它)

    在尝试了更多的东西后,我发现了 jdbcTemplate,这对我来说非常有效,现在插入速度比使用 saveAll() 快得多 覆盖有趣的导入(文件:MultipartFile){

            val inputStream = file.inputStream
            var import: List<Street> = listOf()
            tsvReader.open(inputStream) {
                val csvContents = readAllWithHeaderAsSequence()
                val dataClasses = grass<ImportStreet>().harvest(csvContents)
    
                dataClasses.forEach { row ->
                    import = import + toStreet(row)
                }
                println("Data Converted")
            }
            batchInsert(import)
    
            inputStream.close()
        }
    

    batchInstert 方法正在使用 jdbcTemplate.batchUpdate() 乐趣

    fun batchInsert(streets: List<Street>): IntArray? {
                return jdbcTemplate.batchUpdate(
                        "INSERT INTO street (name, municipalityId) VALUES (?, ?)",
                         object: BatchPreparedStatementSetter {
                            @Throws(SQLException::class)
                            override fun setValues(ps: PreparedStatement, i: Int) {
                                ps.setString(1, streets[i].name)
                                streets[i].municipalityId?.let { ps.setInt(2, it) }
                            }
        
                            override fun getBatchSize(): Int {
                                return streets.size
                            }
                        })
            }
    

    【讨论】:

      猜你喜欢
      • 2011-11-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-13
      • 2011-11-26
      • 1970-01-01
      • 1970-01-01
      • 2014-02-20
      相关资源
      最近更新 更多