«1. Обзор

В этом кратком руководстве мы узнаем о разнице в производительности между методами save() и saveAll() в Spring Data.

2. Приложение

Для проверки производительности нам понадобится приложение Spring с сущностью и репозиторием.

Давайте создадим объект книги:

@Entity
public class Book {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    private String title;
    private String author;

    // constructors, standard getters and setters
}

Кроме того, давайте создадим для него репозиторий:

public interface BookRepository extends JpaRepository<Book, Long> {
}

3. Производительность

Чтобы проверить производительность, мы сохраним 10 000 книг, используя оба методы.

Сначала мы воспользуемся методом save():

for(int i = 0; i < bookCount; i++) {
    bookRepository.save(new Book("Book " + i, "Author " + i));
}

Затем мы создадим список книг и воспользуемся методом saveAll(), чтобы сохранить их все сразу:

List<Book> bookList = new ArrayList<>();
for (int i = 0; i < bookCount; i++) {
    bookList.add(new Book("Book " + i, "Author " + i));
}

bookRepository.saveAll(bookList);

~ ~~ В наших тестах мы заметили, что первый метод занял около 2 секунд, а второй — около 0,3 секунды.

Кроме того, когда мы включили пакетные вставки JPA, мы наблюдали снижение производительности метода save() на 10 % и увеличение производительности метода saveAll() на 60 %.

4. Различия

Изучая реализацию двух методов, мы видим, что saveAll() перебирает каждый элемент и использует метод save() на каждой итерации. Это означает, что не должно быть такой большой разницы в производительности.

При более внимательном рассмотрении мы видим, что оба метода снабжены аннотацией @Transactional.

Кроме того, тип распространения транзакций по умолчанию является НЕОБХОДИМЫМ, что означает, что, если он не указан, новая транзакция создается каждый раз при вызове методов.

В нашем случае каждый раз, когда мы вызываем метод save(), создается новая транзакция, тогда как при вызове saveAll() создается только одна транзакция, которая позже повторно используется функцией save().

Эти накладные расходы приводят к разнице в производительности, которую мы заметили ранее.

Наконец, накладные расходы больше при включении пакетной обработки из-за того, что она выполняется на уровне транзакции.

5. Заключение

В этой статье мы узнали о разнице в производительности между методами save() и saveAll() в Spring Data.

В конечном счете выбор того или иного метода может сильно повлиять на производительность приложения.

Как всегда, код этих примеров доступен на GitHub.