Массовая вставка или обновление с Hibernate? - PullRequest
20 голосов
/ 08 сентября 2011

Мне нужно использовать довольно большие объемы данных из ежедневного файла CSV.CSV содержит около 120 тысяч записей.Это замедляет работу при использовании спящего режима.По сути, кажется, что hibernate делает SELECT перед каждым INSERT (или UPDATE) при использовании saveOrUpdate ();для каждого экземпляра, сохраняемого с помощью saveOrUpdate (), SELECT выдается до фактического INSERT или UPDATE.Я могу понять, почему это происходит, но это ужасно неэффективно для массовой обработки, и я ищу альтернативы

Я уверен, что проблема с производительностью связана с тем, как я использую для этого спящий режим,так как я получил другую версию, работающую с собственным SQL (который анализирует CSV таким же образом) и его буквально бегающие круги вокруг этой новой версии)

Таким образом, к настоящему вопросу, делает ли Hibernate альтернативу MySQL?INSERT ... ON DUPLICATE "Синтаксис существует?

Или, если я решу использовать собственный SQL для этого, могу ли я сделать собственный SQL в транзакции гибернации?То есть будет ли он поддерживать коммит / откат?

Ответы [ 6 ]

31 голосов
/ 08 сентября 2011

Существует множество возможных узких мест в массовых операциях. Лучший подход во многом зависит от того, как выглядят ваши данные. Ознакомьтесь с разделом Hibernate Manual по пакетной обработке.

Как минимум, убедитесь, что вы используете следующий шаблон (скопированный из руководства):

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

for ( int i=0; i<100000; i++ ) {
Customer customer = new Customer(.....);
session.save(customer);
    if ( i % 20 == 0 ) { //20, same as the JDBC batch size
        //flush a batch of inserts and release memory:
        session.flush();
        session.clear();
    }
}

tx.commit();
session.close();

Если вы отображаете плоский файл на очень сложный объектный граф, вам, возможно, придется проявить больше креативности, но основной принцип заключается в том, что вы должны находить баланс между передачей больших объемов данных в базу данных с каждым сбросом / зафиксировать и избежать взрыва размера кэша уровня сеанса.

Наконец, если вам не нужен Hibernate для обработки каких-либо коллекций или каскадирования для правильной вставки данных, рассмотрите возможность использования StatelessSession .

5 голосов
/ 08 сентября 2011

Согласно ответу на аналогичный вопрос , это можно сделать с помощью , сконфигурировав Hibernate для вставки объектов, используя пользовательскую хранимую процедуру , которая использует upsert вашей базы данных функциональность. Это не красиво, хотя.

3 голосов
/ 26 марта 2014

С Hibernate Batch Processing Для обновления я использовал следующее:

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

ScrollableResults employeeCursor = session.createQuery("FROM EMPLOYEE")
                                   .scroll();
int count = 0;

while ( employeeCursor.next() ) {
   Employee employee = (Employee) employeeCursor.get(0);
   employee.updateEmployee();
   seession.update(employee); 
   if ( ++count % 50 == 0 ) {
      session.flush();
      session.clear();
   }
}
tx.commit();
session.close();

Но для вставки я бы выбрал jcwayne answer

1 голос
/ 13 июля 2018

Если вы хотите импортировать данные только без какой-либо обработки или преобразования, то такой инструмент, как PostgreSQL COPY, является самым быстрым способом импорта данных.

Однако, если вам нужно выполнить преобразование, агрегирование данных, корреляцию / объединение существующих и входящих данных, вам потребуется пакетная обработка на уровне приложения.

В этом случае, как я объяснил в этой статье , вы хотите регулярно сбрасывать-очищать-фиксировать:

int entityCount = 50;
int batchSize = 25;

EntityManager entityManager = entityManagerFactory()
    .createEntityManager();

EntityTransaction entityTransaction = entityManager
    .getTransaction();

try {
    entityTransaction.begin();

    for (int i = 0; i < entityCount; i++) {
        if (i > 0 && i % batchSize == 0) {
            entityTransaction.commit();
            entityTransaction.begin();

            entityManager.clear();
        }

        Post post = new Post(
            String.format("Post %d", i + 1)
        );

        entityManager.persist(post);
    }

    entityTransaction.commit();
} catch (RuntimeException e) {
    if (entityTransaction.isActive()) {
        entityTransaction.rollback();
    }
    throw e;
} finally {
    entityManager.close();
}

Также убедитесь, что вы включили пакетную обработку JDBC, используя следующие свойства конфигурации:

<property
    name="hibernate.jdbc.batch_size"
    value="25"
/>

<property
    name="hibernate.order_inserts"  
    value="true"
/>

<property
    name="hibernate.order_updates"  
    value="true"
/>

Для получения более подробной информации об этих свойствах конфигурации Hibernate, ознакомьтесь с этой статьей .

1 голос
/ 08 мая 2013

Если вы используете последовательность или собственный генератор, Hibernate будет использовать select для получения идентификатора:

<id name="id" column="ID">
    <generator class="native" />
</id>

Вам следует использовать генератор hilo или seqHiLo:

<id name="id" type="long" column="id">  
    <generator class="seqhilo">
        <param name="sequence">SEQ_NAME</param>
        <param name="max_lo">100</param>
    </generator>
</id>
0 голосов
/ 27 апреля 2012

Дополнительный выбор - создание уникального идентификатора для ваших данных.

Переключитесь на генерацию последовательности HiLo, и вы сможете уменьшить количество обращений к базе данных по последовательности на номер размера выделения. Обратите внимание, что в первичных ключах будет пробел, если вы не настроите значение последовательности для генератора HiLo

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