Портативная вставка JPA Batch / Bulk

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

В двух словах, метод Dao, вызываемый в цикле для вставки каждой из новых сущностей, выполняет «entityManager.merge(object);».

Разве в спецификациях JPA не определен способ передачи списка сущностей методу Dao и выполнения массовой/пакетной вставки вместо вызова слияния для каждого отдельного объекта?

Кроме того, поскольку метод Dao аннотирован с помощью "@Transactional", мне интересно, происходит ли каждый отдельный вызов слияния в своей собственной транзакции... что не поможет производительности.

Есть идеи?


person Lancelot    schedule 01.09.2009    source источник


Ответы (2)


Нет, в vanilla JPA нет операции пакетной вставки.

Да, каждая вставка будет выполняться в рамках отдельной транзакции. Атрибут @Transactional (без квалификаторов) означает уровень распространения REQUIRED (создайте транзакцию, если она еще не существует). Предполагая, что у вас есть:

public class Dao {
  @Transactional
  public void insert(SomeEntity entity) {
    ...
  }
}

ты делаешь это:

public class Batch {
  private Dao dao;

  @Transactional
  public void insert(List<SomeEntity> entities) {
    for (SomeEntity entity : entities) {
      dao.insert(entity);
    }
  }

  public void setDao(Dao dao) {
    this.dao = dao;
  }
}

Таким образом, вся группа вставок помещается в одну транзакцию. Если вы говорите об очень большом количестве вставок, вы можете разбить его на группы по 1000, 10000 или что-то еще, поскольку достаточно большая незафиксированная транзакция может привести к нехватке ресурсов в базе данных и, возможно, к сбою только из-за размера.

Примечание. @Transactional — это аннотация Spring. См. раздел Управление транзакциями в справочнике Spring.

person cletus    schedule 01.09.2009
comment
Спасибо за ответ. В моем методе Dao было бы полезно использовать Persist() вместо Merge(), поскольку они являются новыми объектами? - person Lancelot; 01.09.2009
comment
merge() и persist() имеют разную семантику. Это не простая проблема, и я настоятельно рекомендую почитать о JPA. - person cletus; 01.09.2009

Если бы вы были в лукавом настроении, вы могли бы сделать следующее:

@Entity
public class SomeEntityBatch {

    @Id
    @GeneratedValue
    private int batchID;
    @OneToMany(cascade = {PERSIST, MERGE})
    private List<SomeEntity> entities;

    public SomeEntityBatch(List<SomeEntity> entities) {
        this.entities = entities;
    }

}

List<SomeEntity> entitiesToPersist;
em.persist(new SomeEntityBatch(entitiesToPersist));
// remove the SomeEntityBatch object later

Из-за каскада объекты будут вставлены в одну операцию.

Я сомневаюсь, что в этом есть какое-то практическое преимущество перед простым сохранением отдельных объектов в цикле. Было бы интересно посмотреть на SQL, выдаваемый реализацией JPA, и сравнить его.

person Tom Anderson    schedule 26.06.2012