Inserções de lote JPA de dados de primavera

Inserções de lote JPA de dados de primavera

1. Visão geral

Sair para o banco de dados é caro. Podemos melhorar o desempenho e a consistência agrupando várias inserções em uma.

Neste tutorial, veremos como fazer isso comSpring Data JPA.

2. Repositório JPA da Primavera

Primeiro, precisamos de uma entidade simples. Vamos chamá-lo deCustomer:

@Entity
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String firstName;
    private String lastName;

    // constructor, getters, setters
}

E então, precisamos do nosso repositório:

public interface CustomerRepository extends CrudRepository {
}

Isso expõe um métodosaveAll para nós, que irá agrupar várias inserções em uma.

Então, vamos aproveitar isso em um controlador:

@RestController
public class CustomerController {
    @Autowired
    CustomerRepository customerRepository;

    @PostMapping("/customers")
    public ResponseEntity 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 customers = Arrays.asList(c1, c2, c3, c4);
        customerRepository.saveAll(customers);
        return ResponseEntity.created("/customers");
    }

    // ... @GetMapping to read customers
}

3. Testando nosso endpoint

Testar nosso código é simples comMockMvc:

@Autowired
private MockMvc mockMvc;

@Test
public void whenInsertingCustomers_thenCustomersAreCreated() throws Exception {
    this.mockMvc.perform(post("/customers"))
      .andExpect(status().isCreated()));
}

4. Temos certeza que estamos em lote?

So, actually, there is a just a bit more configuration to do - vamos fazer uma demonstração rápida para ilustrar a diferença.

Primeiro, vamos adicionar a seguinte propriedade aapplication.properties para ver algumas estatísticas:

spring.jpa.properties.hibernate.generate_statistics=true

Neste ponto, se executarmos o teste, veremos estatísticas como as seguintes:

11232586 nanoseconds spent preparing 4 JDBC statements;
4076610 nanoseconds spent executing 4 JDBC statements;
0 nanoseconds spent executing 0 JDBC batches;

Então, criamos quatro clientes, o que é ótimo,but note that none of them were inside a batch.

O motivo é que o envio em lote não é ativado por padrão em alguns casos.

No nosso caso, é porque estamos usando geração automática de id. Então,by default, saveAll does each insert separately.

Então, vamos ligar:

spring.jpa.properties.hibernate.jdbc.batch_size=4
spring.jpa.properties.hibernate.order_inserts=true

A primeira propriedade diz ao Hibernate para coletar inserções em lotes de quatro. A propriedadeorder_inserts diz ao Hibernate para reservar um tempo para agrupar as inserções por entidade, criando lotes maiores.

Portanto, na segunda vez que executarmos nosso teste, veremos que as inserções foram agrupadas:

16577314 nanoseconds spent preparing 4 JDBC statements;
2207548 nanoseconds spent executing 4 JDBC statements;
2003005 nanoseconds spent executing 1 JDBC batches;

Podemos aplicar a mesma abordagem para exclusões e atualizações (remembering that Hibernate also has an order_updates property).

5. Conclusão

Com a capacidade de inserir lotes, podemos ver alguns ganhos de desempenho.

Obviamente, precisamos estar cientes de que o lote é desativado automaticamente em alguns casos, e devemos verificar e planejar isso antes de enviarmos.

Certifique-se de verificar todos esses trechos de códigoover on GitHub.