JPA / Hibernate объемная (пакетная) вставка - PullRequest
24 голосов
/ 05 мая 2010

Вот простой пример, который я создал после прочтения нескольких тем о массовых вставках jpa, у меня есть 2 постоянных объекта: Пользователь и Сайт. У одного пользователя может быть много сайтов, поэтому здесь у нас отношения один ко многим. Предположим, я хочу создать пользователя и создать / связать несколько сайтов с учетной записью пользователя. Вот как выглядит код, учитывая мое желание использовать массовую вставку для объектов Site.

User user = new User("John Doe");

user.getSites().add(new Site("google.com", user));
user.getSites().add(new Site("yahoo.com", user));

EntityTransaction tx = entityManager.getTransaction();
tx.begin();
entityManager.persist(user);
tx.commit();

Но когда я запускаю этот код (я использую hibernate как поставщик реализации jpa), я вижу следующий вывод sql:

Hibernate: insert into User (id, name) values (null, ?)
Hibernate: call identity()
Hibernate: insert into Site (id, url, user_id) values (null, ?, ?)
Hibernate: call identity()
Hibernate: insert into Site (id, url, user_id) values (null, ?, ?)
Hibernate: call identity()

Итак, я имею в виду "настоящая" массовая вставка не работает или я запутался?

Вот исходный код для этого примера проекта, это проект maven, поэтому вам нужно только скачать и запустить mvn install, чтобы проверить вывод.

ОБНОВЛЕНИЕ:

После любезного совета Кена Лю я отключил автоматическую генерацию идентификатора объекта сайта:

    User user = new User("John Doe");
    user.getSites().add(new Site(1, "google.com", user));
    user.getSites().add(new Site(2, "yahoo.com", user));
    entityManager.setFlushMode(FlushModeType.COMMIT);
    EntityTransaction tx = entityManager.getTransaction();
    tx.begin();
    entityManager.persist(user);
    tx.commit();

Теперь у меня есть следующая строка в отладочном выводе:

DEBUG: org.hibernate.jdbc.AbstractBatcher - Выполнение размера пакета: 2

Работает!

Ответы [ 3 ]

19 голосов
/ 05 мая 2010

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

6 голосов
/ 29 октября 2014

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

Несмотря на то, что вы временно утратили удобство своего ORM, выигрыш является значительным, особенно если у вас есть собственные сгенерированные идентификаторы, поскольку hibernate обычно выполняет один SELECT для каждого INSERT.

Session.doWork очень удобно для облегчения этого.

private MyParentObject saveMyParentObject(final MyParentObject parent, final List<MyChildObject> children)
{
    transaction = session.beginTransaction();
    try
    {
        session.save(parent); // NOTE: parent.parentId assigned and returned here

        session.doWork(new Work()
        {
            public void execute(Connection con) throws SQLException
            {
                // hand written insert SQL - can't use hibernate
                PreparedStatement st = con.prepareStatement("INSERT INTO my_child (parent_id, name, ...) values (?, ?, ...)");

                for (MyChildObject child : children)
                {
                    MyChildObject child = new MyChildObject();
                    child.setParentId(parent.getParentId()); // assign parent id for foreign key

                    // hibernate can't help, determine jdbc parameters manually
                    st.setLong(1, child.getParentId());
                    st.setString(2, child.getName());
                    ...
                    st.addBatch();
                }

                // NOTE: you may want to limit the size of the batch
                st.executeBatch();
            }
        });

        // if your parent has a OneToMany relationship with child(s), refresh will populate this 
        session.refresh(parent);
        transaction.commit();
        return parent;
    }
    catch(Throwable e)
    {
        transaction.rollback();
        throw new RuntimeException(e);
    }   
}
6 голосов
/ 09 марта 2011

Я написал небольшой блог, в котором рассказывается о проблемах пакетной вставки, а также есть указатель на небольшой проект, который имеет все необходимые конфигурации для начала пакетной вставки с помощью Hibernate. Подробности смотрите на http://sensiblerationalization.blogspot.com/2011/03/quick-tip-on-hibernate-batch-operation.html

...