@danieldestro escreveu:
Estou tendo um problema com JPA / Hibernate onde ele está executando um INSERT seguido de um UPDATE para um novo registro que ele persiste.
Quero eliminar a execução do UPDATE, pois não faz sentido nenhum ele executar este segundo comando, pois não estou explicitamente fazendo isso. Alguma ideia do que pode causar essa redundância?
O modelo de entidade é assim (sem entrar no mérito do design):
PriceSummary [1] --> [N] ServicePriceSummary [1] --> [N] ServicePriceBreakdown [1] --> [N] MonthBreakdown
Devido a uma lógica de negócio particular, pode chegar a 10.000 ou 20.000 (sim 20 mil) registros de MonthBreakdown que são criados nos cálculos para uma grande proposta.
Fazendo um profiling da nossa aplicação, este é um grande gargalo, onde as operações do MonthBreakdown de INSERT e UPDATE, juntas, representam mais de 90% do processamento.
Se conseguir eliminar o UPDATE em já reduz em quase 50% o tempo de processamento.
Meu código (resumido) é, basicamente, o seguinte:
@Service @Transactional public class PricingService { /** Método chamado para realizar a transação. */ public PricingContext calculate(Integer id) { Proposal proposal = proposalRepository.findOne(id); PriceSummary summary = new PriceSummary(proposal); PricingContext context = new PricingContext(summary); List<Service> services = serviceRepository.findByProposalId(id); services.forEach(service -> calculate(service, context)); priceSummaryRepository.saveAndFlush(summary); // JpaRepository interface return context; } public void calculate(Service service, PricingContext context) { List<Rates> rates = ratesService.findRatesForProposal(context.getProposal()); // do some JPA queries and return data // faz uns calculos muito loucos aqui e atualiza PricingSummary em context } }
Entidade PriceSummary:
@Entity @Table(name="price_summary") public class PriceSummary { @Id @Column(name = "id") private Integer id; @Column(name = "lock_version") private Integer lockVersion; @Column(name = "price") private BigDecimal price; @OneToMany(cascade = CascadeType.ALL, mappedBy = "priceSummary", orphanRemoval = true) private final List<ServicePriceSummary> servicePriceSummaries = new ArrayList<>(); // getters & setters }
Entidade ServicePriceSummary:
@Entity @Table(name="srv_price_summary") public class ServicePriceSummary { @Id @Column(name = "id") private Integer id; @ManyToOne @JoinColumn(name = "price_summary_id") private PriceSummary priceSummary; @OneToMany(cascade = CascadeType.ALL, mappedBy = "serviceOfferingPriceSummary", orphanRemoval = true) private final List<ServicePriceBreakdown> servicePriceBreakdowns = new ArrayList<>(); // getters & setters }
Entidade ServicePriceBreakdown:
@Entity @Table(name="srv_price_breakdown") public class ServicePriceBreakdown { @Id @Column(name = "id") private Integer id; @ManyToOne @JoinColumn(name = "srv_price_summary_id") private ServicePriceSummary servicePriceSummary; @Column(name = "country_code") private String countryCode; @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "servicePriceBreakdown", orphanRemoval = true) private final List<MonthBreakdown> monthBreakdowns = new ArrayList<>(); // getters & setters }
Entidade MonthBreakdown:
@Entity @Table(name="month_breakdown") public class MonthBreakdown { @Id @Column(name = "id") private Integer id; @ManyToOne @JoinColumn(name = "srv_price_breakdown_id") private ServicePriceBreakdown servicePriceBreakdown; @Column(name = "month_number") private Integer month; @Column(name = "cost") private BigDecimal cost; // getters & setters }
Curiosamente, se olhar o relatório do profiling do JProfiler, parece que ele executa o INSERT e depois o UPDATE quando se adiciona o registro em uma lista de um entidade. Porém no log SQL do Hibernate ele primeiro lista todos os INSERT e em seguida todos os UPDATE.
Outra estratégia foi configurar o Batch Size do Hibernate. Com isso tivemos algum ganho (20 a 40% em alguns casos), mas ainda não resolveu o problema.
Uma outra estratégia que pensamos foi remover a configuração de "cascade = CascadeType.ALL" da classe ServicePriceBreakdown no atributo monthBreakdowns e não deixar o Hibernate executar os INSERTS para o MonthBreakdown. Então, após salvar os objetos do grafo, reuno uma lista dos MonthBreakdown e explicitamente salvo, visando evita o UPDATE e melhorar execução em batch. Faz sentido?
Estamos usando OpenJDK 1.8, Spring Boot 1.3.1, Tomcat 7.
Mensagens: 1
Participantes: 1