Пакетная вставка не работает должным образом, используя пружинную загрузку, спящий режим и MySQL - PullRequest
0 голосов
/ 02 декабря 2019

У меня есть одна очень раздражающая проблема, я прочитал всю существующую документацию онлайн и прочитал все вопросы и ответы по stackoverflow, связанные с этой темой, но просто не могу заставить это работать!

Я действительно в отчаянии и не знаю, чего мне не хватает, поэтому я постараюсь дать вам все, что у меня есть. По сути, я пытаюсь сохранить много данных одним запросом вместо нескольких запросов для каждого объекта. Как вы можете подозревать, я использую Spring Boot, Hibernate и MySql.

Итак, основные факты, которые я узнал на основе того, что я прочитал о «пакетной вставке с использованием mysql + hibernate», следующие:

  1. Mysql не поддерживает идентификатор последовательности, поэтому я не могу использовать его, как я мог бы использовать его для 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

так что в основном это должно показать мне запросы, которые выполняются в БД, и там я вижу, что для каждого объекта person у меня есть один оператор вставки.

enter image description here

По сути, результат этого запроса:

SELECT
    *
FROM
    mysql.general_log;

И там я отчетливо вижу, что у меня есть несколько операторов вставки, которые выполняют один запрос на объект (персона).

Edit: 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)]

У меня есть несколько записей, подобных этой.

Заранее спасибо за любую помощь!

1 Ответ

0 голосов
/ 03 декабря 2019

Просто чтобы дать ответ на случай, если кому-то это понадобится:

Короче говоря, я не смог заставить работать пакетную обработку 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!!.update { connection ->
        connection.autoCommit = false
        val ps: PreparedStatement = connection.prepareStatement(INSERT_SQL_PARAMS)

        configParams.forEachIndexed { index, it ->
            ps.setLong(1, it.configurationId)
            ps.setString(2, it.pKey)
            ps.setString(3, it.pValue)
            ps.addBatch()

            if (index != 0 && index % 1000 == 0) {
                ps.executeBatch()
                connection.commit()
            }
        }

        ps.executeBatch()
        connection.commit()
        ps
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...