Spring Data JPA: Inserção em lote para entidades aninhadas

Eu tenho um caso de teste em que preciso persistir 100.000 instâncias de entidades no database. O código que estou usando atualmente faz isso, mas leva 40 segundos até que todos os dados sejam mantidos no database. Os dados são lidos de um arquivo JSON que tem cerca de 15 MB de tamanho.

Agora eu já havia implementado um método de inserção em lote em um repository personalizado antes de outro projeto. No entanto, nesse caso, eu tinha muitas entidades de nível superior para persistir, com apenas algumas entidades aninhadas.

No meu caso atual eu tenho 5 entidades Job que contêm uma lista de cerca de 30 entidades JobDetail . Um JobDetail contém entre 850 e 1100 entidades JobEnvelope .

Ao gravar no database, save(Iterable jobs) entidades List of Job com o método de interface save(Iterable jobs) padrão. Todas as entidades aninhadas têm o PERSIST CascadeType. Cada entidade tem sua própria tabela.

A maneira usual de ativar inserções em lote seria implementar um método personalizado, como saveBatch que é saveBatch vez em quando. Mas o meu problema neste caso são as entidades JobEnvelope . Eu não os JobEnvelope com um repository JobEnvelope , em vez disso, deixo o repository da entidade Job manipulá-lo. Estou usando o MariaDB como servidor de database.

Então, minha pergunta se resume ao seguinte: Como posso fazer o JobRepository inserir suas entidades aninhadas em lotes?

Estas são as minhas 3 inputs em questão:

Trabalho

 @Entity public class Job { @Id @GeneratedValue private int jobId; @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST, mappedBy = "job") @JsonManagedReference private Collection jobDetails; } 

JobDetail

 @Entity public class JobDetail { @Id @GeneratedValue private int jobDetailId; @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST) @JoinColumn(name = "jobId") @JsonBackReference private Job job; @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST, mappedBy = "jobDetail") @JsonManagedReference private List jobEnvelopes; } 

JobEnvelope

 @Entity public class JobEnvelope { @Id @GeneratedValue private int jobEnvelopeId; @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST) @JoinColumn(name = "jobDetailId") private JobDetail jobDetail; private double weight; } 

Certifique-se de configurar corretamente as propriedades relacionadas ao lote do Hibernate:

 100 true true 

O ponto é que instruções sucessivas podem ser agrupadas se manipularem a mesma tabela. Se houver a instrução fazendo a inserção em outra tabela, a construção do lote anterior deve ser interrompida e executada antes dessa instrução. Com a propriedade hibernate.order_inserts você está dando permissão para o Hibernate reordenar inserções antes de construir instruções batch ( hibernate.order_updates tem o mesmo efeito para instruções de atualização).

jdbc.batch_size é o tamanho máximo do lote que o Hibernate irá usar. Experimente e analise valores diferentes e escolha um que mostre o melhor desempenho em seus casos de uso.

Observe que o envio em lote de instruções de inserção está desativado se o gerador IDENTITY id for usado.

Específico para o MySQL, você precisa especificar rewriteBatchedStatements=true como parte da URL de conexão. Para garantir que os lotes estejam funcionando como esperado, adicione profileSQL=true para inspecionar o SQL que o driver envia ao database. Mais detalhes aqui .

Se suas entidades forem versionadas (para fins de bloqueio otimista), para usar as atualizações em lote (não afeta as inserções), você precisará ativar também:

 true 

Com essa propriedade, você informa ao Hibernate que o driver JDBC é capaz de retornar a contagem correta de linhas afetadas ao executar a atualização em lote (necessária para executar a verificação de versão). Você tem que verificar se isso funciona corretamente para o seu driver de database / jdbc. Por exemplo, ele não funciona no Oracle 11 e em versões mais antigas do Oracle.

Você também pode querer limpar e limpar o contexto de persistência após cada lote para liberar memory, caso contrário, todos os objects gerenciados permanecerão no contexto de persistência até que ele seja fechado.

Além disso, você pode achar este blog útil, pois explica bem os detalhes do mecanismo de lotes do Hibernate.