【问题标题】:Batch insert is not working properly using spring boot, hibernate and mysql使用spring boot,hibernate和mysql批量插入无法正常工作
【发布时间】:2020-03-26 13:25:52
【问题描述】:

我有一个非常烦人的问题,我已经阅读了所有现有的在线文档并阅读了与该主题相关的所有 stackoverflow 问题和答案,但根本无法使其正常工作!

我真的很绝望,我不知道我错过了什么,所以我会尽力给你我到目前为止所拥有的一切。基本上我想做的是用一个查询而不是每个对象的多个查询来保存大量数据。你可能会怀疑我正在使用 Spring Boot、Hibernate 和 MySql。

根据我所读到的与“使用 mysql + hibernate 进行批量插入”相关的内容,我目前所了解的基本事实如下:

  1. Mysql 不支持Sequence ID,所以我不能使用它,就像我可以将它用于PostgreSql
  2. Hibernate 不支持开箱即用的批量插入,需要添加几个应用属性

这就是我目前所拥有的:

我添加的应用程序属性:

spring.datasource.url=jdbc:mysql://localhost:32803/db?rewriteBatchedStatements=true
spring.jpa.properties.hibernate.jdbc.batch_size=50
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
spring.jpa.open-in-view=false
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.order_updates=true
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.batch_versioned_data=true
spring.jpa.properties.hibernate.id.new_generator_mappings=false
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.type=trace
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext


@Entity
data class Person (
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        val id: Long?,

        var firstName: String,

        var lastName: String,

        var country: String,

        var org: Int
)

我想要的是一次保存很多人,如您所见,我添加了 50 个批量大小,如果我理解正确,这意味着我将在保存时每 50 人执行一次数据库命中。 (如果我错了,请纠正我)

最后我有我执行批量插入的存储库:

@Repository
class PersonRepositoryCustomImpl : PersonRepositoryCustom {

    @PersistenceContext
    private val entityManager: EntityManager? = null

    @Transactional
    override fun batchSave2(persons: Set<Person>) {
        val session = entityManager!!.unwrap(Session::class.java)


        persons.forEachIndexed { index, person ->
            if ( index % 50 == 0 ) {
                session!!.flush()
                session.clear()
            }

            session!!.save(person)
        }

        session.close()
    }

    @Transactional
    override fun <T : Person?> batchSave(entities: Collection<T>): Collection<T>? {
        val savedEntities: MutableList<T> = ArrayList(entities.size)
        var i = 0
        for (t in entities) {
            savedEntities.add(persistOrMerge(t))
            i++
            if (i % 50 == 0) { // Flush a batch of inserts and release memory.
                entityManager!!.flush()
                entityManager.clear()
            }
        }
        return savedEntities
    }

    private fun <T : Configuration?> persistOrMerge(t: T): T {
        return if (t!!.id == null) {
            entityManager!!.persist(t)
            t
        } else {
            entityManager!!.merge(t)
        }
    }
}

所以在这里你可以看到我已经尝试以几乎相同的方式在 2 上工作,但当然它们似乎都不起作用。

为了确认我实际上是在进行批量插入,我正在查看:

https://tableplus.com/blog/2018/10/how-to-show-queries-log-in-mysql.html

所以基本上这应该显示正在数据库上执行的查询,并且我可以看到对于每个人对象我都有一个插入语句。

基本上就是这个查询的结果:

SELECT
    *
FROM
    mysql.general_log;

我可以清楚地看到我有多个插入语句,每个对象(人)执行一个查询。

编辑: https://blog.arnoldgalovics.com/configuring-a-datasource-proxy-in-spring-boot/

我还实现了数据源代理,这证明我没有做批量插入:

Name:, Time:1, Success:True, Type:Prepared, Batch:False, QuerySize:1, BatchSize:0, Query:["insert into person(firstName, lastName, country, org) values (?, ?, ?, ?)"], Params:[(10,John,Johny,USA,ORG)]

我有多个这样的记录。

提前感谢您的任何帮助!

【问题讨论】:

  • 您看到 N 个查询的事实并不意味着 Hibernate 没有使用批量插入。所有查询都在批处理语句中一次发送。 Google for "JDBC batch statement" 知道什么是批处理语句。
  • 我如何确认我正在做批量插入?我知道在应用程序的控制台上休眠正在为每个对象发送一个,但这是我在数据库端执行的日志
  • 如果您不信任休眠文档,显然使用 datasource-proxy 可以做到这一点:docs.jboss.org/hibernate/orm/current/userguide/html_single/…
  • 非常感谢,我也确认了这一点,每个对象插入一个:(
  • 啊,但您显然正在使用自动增量 ID。使用表生成。 AFAIR,自动增量强制 Hibernate 分别执行每个语句,以获取数据库分配给每个实体的主键。

标签: mysql hibernate spring-boot jpa jdbc


【解决方案1】:

只是在有人需要时给出答案:

长话短说,我无法让 MySql + hibernate 批处理工作,为了测试,我实际上能够使其与 PostgreSQL 一起工作。

但无论如何,如果有人需要使用 MySql,有一种方法可以使用 JDBC 批处理,而且代码或多或少都非常简单:

private String INSERT_SQL_PARAMS = "INSERT INTO item_params(p_key, p_value, item_id) values (?,?,?)"

override fun saveParams(configParams: Set<ItemParam>) {
    jdbcTemplate!!.batchUpdate(INSERT_SQL_PARAMS , configParams, 3000) { ps, argument ->
        ps.setLong(1, argument.pKey)
        ps.setString(2, argument.pValue)
        ps.setString(3, argument.itemId)
    }
}

【讨论】:

    猜你喜欢
    • 2018-03-20
    • 2013-09-30
    • 2016-03-17
    • 2019-02-09
    • 2018-10-22
    • 2012-03-31
    • 1970-01-01
    • 2018-11-14
    • 2021-11-02
    相关资源
    最近更新 更多