Кейс: saveAll() в Spring Data JPA писал по одной строке, а не батчем
Ожидали, что saveAll() на 5000 сущностей соберёт несколько batch-INSERT и уйдёт в базу за секунды. В логе Hibernate — 5000 отдельных INSERT, каждый со своим round-trip.
Причина — GenerationType.IDENTITY у @Id. Hibernate обязан узнать сгенерированный id сразу после вставки строки, чтобы отдать его вызывающему коду. Батчинг INSERT для IDENTITY отключён на уровне Hibernate независимо от hibernate.jdbc.batch_size.
Переход на GenerationType.SEQUENCE с allocationSize вернул батчинг: Hibernate заранее резервирует диапазон id пачкой и не ждёт ответа БД на каждую вставку.
@Id @GeneratedValue( strategy = GenerationType.SEQUENCE, generator = "seq") @SequenceGenerator( name = "seq", sequenceName = "entity_seq", allocationSize = 50) private Long id;
Спросят следом: почему allocationSize в аннотации должен совпадать с INCREMENT BY в самой sequence в БД. Если не совпадает — Hibernate резервирует диапазон в памяти, а sequence в базе прыгает с другим шагом, id могут разъехаться между инстансами приложения.
IDENTITY удобен на старте проекта и становится узким местом ровно тогда, когда объём вставок вырастает.
Тренажёр: 600 вопросов, мок с таймером, план повторов
senior·base — что спрашивают на самом деле
В этом посте были ссылки, но мы их удалили по правилам Сетки