1. Обзор
Выход в базу стоит дорого. Возможно, мы сможем улучшить производительность и согласованность, объединив несколько вставок в одну.
В этом уроке мы рассмотрим, как это сделать с помощью Spring Data JPA .
2. Весенний репозиторий JPA
Во-первых, нам понадобится простая сущность. Назовем его Клиент
:
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
// constructor, getters, setters
}
И затем нам нужен наш репозиторий:
public interface CustomerRepository extends CrudRepository<Customer, Long> {
}
Это предоставляет нам метод saveAll , который объединяет несколько вставок в одну.
Итак, давайте воспользуемся этим в контроллере:
@RestController
public class CustomerController {
@Autowired
CustomerRepository customerRepository;
@PostMapping("/customers")
public ResponseEntity<String> insertCustomers() {
Customer c1 = new Customer("James", "Gosling");
Customer c2 = new Customer("Doug", "Lea");
Customer c3 = new Customer("Martin", "Fowler");
Customer c4 = new Customer("Brian", "Goetz");
List<Customer> customers = Arrays.asList(c1, c2, c3, c4);
customerRepository.saveAll(customers);
return ResponseEntity.created("/customers");
}
// ... @GetMapping to read customers
}
3. Тестирование нашей конечной точки
Тестировать наш код просто с помощью MockMvc
:
@Autowired
private MockMvc mockMvc;
@Test
public void whenInsertingCustomers_thenCustomersAreCreated() throws Exception {
this.mockMvc.perform(post("/customers"))
.andExpect(status().isCreated()));
}
4. Уверены ли мы, что собираем партии?
Так что, на самом деле, нужно сделать еще немного настройки — давайте сделаем быструю демонстрацию, чтобы проиллюстрировать разницу.
Во-первых, давайте добавим следующее свойство в application.properties
, чтобы увидеть некоторую статистику:
spring.jpa.properties.hibernate.generate_statistics=true
На этом этапе, если мы запустим тест, мы увидим статистику, подобную следующей:
11232586 nanoseconds spent preparing 4 JDBC statements;
4076610 nanoseconds spent executing 4 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;
Итак, мы создали четырех клиентов, и это здорово, но обратите внимание, что ни один из них не был внутри партии.
Причина в том, что пакетная обработка в некоторых случаях не включена по умолчанию.
В нашем случае это потому, что мы используем автоматическую генерацию идентификатора. Таким образом, по умолчанию saveAll
выполняет каждую вставку отдельно.
Итак, включаем:
spring.jpa.properties.hibernate.jdbc.batch_size=4
spring.jpa.properties.hibernate.order_inserts=true
Первое свойство указывает Hibernate собирать вставки партиями по четыре. Свойство order_inserts
сообщает Hibernate, что нужно потратить время на группировку вставок по объектам, создавая большие пакеты.
Итак, во второй раз, когда мы запустим наш тест, мы увидим, что вставки были упакованы:
16577314 nanoseconds spent preparing 4 JDBC statements;
2207548 nanoseconds spent executing 4 JDBC statements;
2003005 nanoseconds spent executing 1 JDBC batches;
Мы можем применить тот же подход к удалениям и обновлениям ( помня, что Hibernate также имеет свойство order_updates
).
5. Вывод
Благодаря возможности пакетной вставки мы можем увидеть некоторый прирост производительности.
Мы, конечно, должны знать, что в некоторых случаях пакетная обработка автоматически отключается, и мы должны проверить и спланировать это перед отправкой.
Обязательно ознакомьтесь со всеми этими фрагментами кода на GitHub .