Gần như chắc chắn entity đang dùng GenerationType.IDENTITY. Hibernate tự động tắt batching JDBC khi id sinh bằng identity column.
Lý do: với IDENTITY, id chỉ có sau khi INSERT chạy. Mà persist() bắt buộc phải trả về entity có id ngay, nên Hibernate bắn INSERT lập tức thay vì xếp vào ActionQueue. Đến lúc flush thì không còn câu lệnh nào để gom lô.
Cách sửa — dùng SEQUENCE với pooled optimizer:
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "order_seq")
@SequenceGenerator(name = "order_seq", sequenceName = "order_seq", allocationSize = 50)
private Long id;spring.jpa.properties.hibernate.jdbc.batch_size=50
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=trueSequence cho phép lấy trước một dải id ở client (allocationSize), nên Hibernate gán id mà không cần chạm DB → các INSERT xếp được vào lô.
Ba điểm cần bổ sung cho luồng import lớn:
- flush() + clear() định kỳ mỗi batch_size bản ghi, nếu không persistence context giữ toàn bộ 100.000 entity kèm snapshot dirty checking và dẫn tới OutOfMemoryError.
- Bật rewriteBatchedStatements=true trên JDBC URL của MySQL, nếu không driver vẫn gửi từng câu.
- Với import thuần ghi, cân nhắc StatelessSession hoặc JdbcTemplate.batchUpdate — bỏ hẳn persistence context.