JPA с вставкой HIBERNATE очень медленно

Я пытаюсь вставить некоторые данные в SQL Server 2008 R2 с помощью JAP и HIBERNATE. Все "работает", за исключением того, что оно очень медленное. Чтобы вставить 20000 строк, требуется около 45 секунд, а С# script занимает менее 1 секунды.

Любой ветеран в этом домене может предложить некоторые подсказки? Я был бы очень признателен.

Обновление: получили некоторые полезные советы из ответов ниже, но он по-прежнему не работает должным образом. Скорость - то же самое.

Вот обновленный файл persistence.xml:

<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="ClusterPersist"
    transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <class>cluster.data.persist.sqlserver.EventResult</class>
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <properties>
        <property name="javax.persistence.jdbc.url"
            value="jdbc:sqlserver://MYSERVER:1433;databaseName=MYTABLE" />
        <property name="javax.persistence.jdbc.user" value="USER" />
        <property name="javax.persistence.jdbc.password" value="PASSWORD" />
        <property name="javax.persistence.jdbc.driver"
            value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
        <property name="hibernate.show_sql" value="flase" />
        <property name="hibernate.hbm2ddl.auto" value="update" />

        <property name="hibernate.connection.provider_class"
            value="org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider" />

        <property name="hibernate.c3p0.max_size" value="100" />
        <property name="hibernate.c3p0.min_size" value="0" />
        <property name="hibernate.c3p0.acquire_increment" value="1" />
        <property name="hibernate.c3p0.idle_test_period" value="300" />
        <property name="hibernate.c3p0.max_statements" value="0" />
        <property name="hibernate.c3p0.timeout" value="100" />
        <property name="hibernate.jdbc.batch_size" value="50" />
        <property name="hibernate.cache.use_second_level_cache" value="false" />
    </properties>
</persistence-unit>

И вот обновленная часть кода:

public static void writeToDB(String filePath) throws IOException {

    EntityManager entityManager = entityManagerFactory.createEntityManager();
    Session session = (Session) entityManager.getDelegate();
    Transaction tx = session.beginTransaction();
    int i = 0;

    URL filePathUrl = null;
    try {
        filePathUrl = new URL(filePath);
    } catch (MalformedURLException e) {
        filePathUrl = (new File(filePath)).toURI().toURL();
    }

    String line = null;
    BufferedReader stream = null;

    try {
        InputStream in = filePathUrl.openStream();
        stream = new BufferedReader(new InputStreamReader(in));


        // Read each line in the file
        MyRow myRow = new MyRow();
        while ((line = stream.readLine()) != null) {
            String[] splitted = line.split(",");
            int num1 = Integer.valueOf(splitted[1]);
            float num2= Float.valueOf(splitted[6]).intValue();

            myRow.setNum1(num1);
            myRow.setNum2(num2);

            session.save(myRow);

            if (i % 50 == 0) { 
                session.flush();
                session.clear();
            }

            i++;

        }
        tx.commit();

    } finally {
        if (stream != null)
            stream.close();
    }
    session.close();

}

Обновлено, вот источник для MyRow:

@Entity
@Table(name="MYTABLE")
public class MyRow {    

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY) 
private Long id;

@Basic
@Column(name = "Num1")
private int Num1;

@Basic
@Column(name = "Num2")
private float Num2;

public Long getId() {
    return id;
}

public void setId(Long id) {
    this.id = id;
}

public float getNum1() {
    return Num1;
}

public void setNum1(float num1) {
    Num1 = num1;
}

public int getNum2() {
    return Num2;
}

public void setNum2(int num2) {
    Num2 = num2;
}
}

Ответ 1

Чтобы включить JDBC batching, вы должны инициализировать свойство hibernate.jdbc.batch_size от 10 до 50 (только int)

hibernate.jdbc.batch_size=50

Если все еще не так быстро, как ожидалось, я бы рассмотрел вышеописанный документ, обращая внимание на ПРИМЕЧАНИЕ и раздел 4.1. Особенно примечание, в котором говорится: "Hibernate отключает встраивание пакетов на уровне JDBC прозрачно, если вы используете генератор идентификационных идентификаторов".

Ответ 2

Режим спящего режима "по умолчанию" медленный.

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

Использовать пакетную обработку вместо http://docs.jboss.org/hibernate/core/4.0/devguide/en-US/html/ch04.html

Ответ 3

Проблема

Одно из главных достижений производительности, если вы используете Hibernate, поскольку ваш ORM - это механизм "грязной проверки".

При промывке грязная проверка должна выполняться на каждом объекте в сеансе, чтобы убедиться, что он "грязный", то есть один из его атрибутов изменился с момента его загрузки из базы данных. Для всех "грязных" (измененных) объектов Hibernate должен генерировать SQL-обновления для обновления записей, представляющих грязные объекты.

Грязная проверка Hibernate, как известно, медленна на чем-то, кроме небольшого количества объектов, потому что ему нужно выполнить сравнение "по полю" между объектами в памяти с моментальным снимком, сделанным, когда объект был сначала загружен из базы данных.

Технические характеристики механизма проверки грязной среды Hibernate

Вы можете узнать больше о механизме грязной проверки Hibernate, реализованном как сравнение "поле за полем" здесь:

Как Hibernate обнаруживает грязное состояние объекта сущности?

Как проблема решается в других ORM

Более эффективный механизм, используемый некоторыми другими ORM, заключается в использовании автоматически генерируемого атрибута "грязного флага" вместо сравнения "по полю", но это традиционно доступно только в ORM, которые используют и продвигают улучшение кода байта или байтовый код "ткачество", как его иногда называют, например, http://datanucleus.org и другие

Во время улучшения кода байта, DataNucleus или любого другого ORM, поддерживающего эту функцию, каждый класс сущностей увеличивается до:

  • добавить неявный атрибут флагов
  • добавьте код в каждый из методов setter в классе, чтобы автоматически установить флаг грязной при вызове

Затем во время флеша нужно проверить только грязный флаг вместо того, чтобы выполнять поле путем сравнения полей, что, как вы можете себе представить, на порядок быстрее.

Другие негативные последствия "поля по полю" грязная проверка

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

Каждый объектный снимок является совокупностью всех его полей.

В дополнение к хиту производительности механизма грязной проверки Hibernate во время стирания этот механизм также обременяет ваше приложение дополнительным потреблением памяти и использованием ЦП, связанными с созданием и инициализацией этих снимков каждого отдельного объекта, загружаемого из базы данных - который может работать в тысячах или миллионах в зависимости от вашего приложения.

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

  • Hibernate традиционно способствовал "отсутствию необходимости улучшения байтового кода" в качестве функции, когда люди оценивают технологии ORM.
  • Исторические проблемы надежности с реализацией расширения кода байта гибернации, которые, возможно, не столь зрелые, как ORM, которые использовали и продвигали усовершенствование байтового кода с самого начала.
  • Некоторые люди все еще боятся использования улучшения кода в байтах из-за продвижения анти-байт-кода, улучшающего позицию и страха определенных групп, которые прививали людям использование байт-кода в первые дни ORM.

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