Problema envolvendo tabela particionada + Spring Boot: GenerationTarget encountered exception accepting command : Error executing DDL
Estou trabalhando com tabelas particionadas usando Postgres e Spring Boot, consegui fazer a partição e nos testes que eu fiz o método GET e POST está funcionando do jeito que eu queria, porém gostaria de ajuda para resolver o seguinte erro, que é retornado quando eu inicio a aplicação:
O passo a passo do que eu fiz antes do erro aparecer:
Criei o Model principal, que fará referência a tabela particionada:
Criei a classe do id composto:
E então iniciei a aplicação, tudo rodou normalmente.
Após isso pausei a aplicação e particionei a tabela ''teste'' seguindo a documentação do Postgres: https://www.postgresql.org/docs/current/ddl-partitioning.html Tópico: 5.11.2.1. Example
E minha tabela ficou da seguinte forma:
Então eu iniciei a aplicação novamente e o seguinte erro foi impresso no log do Spring Boot:
Meu application.properties está assim:
Eu sei que alterar o ''spring.jpa.hibernate.ddl-auto'' para ''none'' resolveria o problema, porém não é a solução que eu desejo, já que não quero gerenciar manualmente todas as alterações no banco de dados.
GenerationTarget encountered exception accepting command : Error executing DDL "create table teste (data date not null, id int4 not null, nome varchar(255), primary key (data, id))" via JDBC Statement org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL "create table teste (data date not null, id int4 not null, nome varchar(255), primary key (data, id))" via JDBC Statement at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.accept(GenerationTargetToDatabase.java:67) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
O passo a passo do que eu fiz antes do erro aparecer:
Criei o Model principal, que fará referência a tabela particionada:
@Entity @Table(name = "teste") @IdClass(TesteId.class) @Getter @Setter @NoArgsConstructor @AllArgsConstructor public class Teste { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "teste_id_seq") @SequenceGenerator(name = "teste_id_seq", sequenceName = "teste_id_seq", initialValue = 1, allocationSize = 1) private int id; @Id private LocalDate data; private String nome; }
Criei a classe do id composto:
@Getter @Setter @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode public class TesteId implements Serializable { private static final long serialVersionUID = 1L; private int id; private LocalDate data; }
E então iniciei a aplicação, tudo rodou normalmente.
Após isso pausei a aplicação e particionei a tabela ''teste'' seguindo a documentação do Postgres: https://www.postgresql.org/docs/current/ddl-partitioning.html Tópico: 5.11.2.1. Example
E minha tabela ficou da seguinte forma:
CREATE TABLE IF NOT EXISTS public.teste ( data date NOT NULL, id integer NOT NULL, nome character varying(255) COLLATE pg_catalog."default", CONSTRAINT teste_pkey PRIMARY KEY (data, id) ) PARTITION BY RANGE (data);
Então eu iniciei a aplicação novamente e o seguinte erro foi impresso no log do Spring Boot:
WARN 16668 --- [ main] o.h.t.s.i.ExceptionHandlerLoggedImpl : GenerationTarget encountered exception accepting command : Error executing DDL "create table teste (data date not null, id int4 not null, nome varchar(255), primary key (data, id))" via JDBC Statement org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL "create table teste (data date not null, id int4 not null, nome varchar(255), primary key (data, id))" via JDBC Statement at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.accept(GenerationTargetTo Database.java:67) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final] at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.applySqlString(AbstractSchemaMigra tor.java:562) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final] at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.applySqlStrings(AbstractSchemaMigr ator.java:507) ~[hibernate-core-5.4.32.Final.jar:5.4.32.Final]
Meu application.properties está assim:
spring.datasource.url=jdbc:postgresql://localhost:5432/teste spring.datasource.username=postgres spring.datasource.password=postgres spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect spring.jpa.hibernate.ddl-auto=update spring.jpa.defer-datasource-initialization=true spring.datasource.driver-class-name=org.postgresql.Driver
Eu sei que alterar o ''spring.jpa.hibernate.ddl-auto'' para ''none'' resolveria o problema, porém não é a solução que eu desejo, já que não quero gerenciar manualmente todas as alterações no banco de dados.
Yago Vieira
Curtidas 0
Respostas
Frank Hosaka
31/05/2023
Eu testei o comando no MySQL
e a mensagem que apareceu foi esse:
Por que você está tentando criar um arquivo que já existe?
create table teste (data date not null, id int4 not null, nome varchar(255), primary key (data, id))
e a mensagem que apareceu foi esse:
Error Code: 1050. Table 'teste' already exists
Por que você está tentando criar um arquivo que já existe?
GOSTEI 0
Yago Vieira
31/05/2023
A questão é o seguinte:
A tabela já existe realmente e eu não quero tentar criar ela novamente, mas é o Hibernate que está tentando fazer isso.
Quando eu inicio a aplicação o Hibernate não tentar criar novamente as tabelas "normais", porém tenta criar novamente a tabela que eu particionei.
Eu recriei o banco de dados várias vezes para fazer testes e só retorna o erro se eu particionar a tabela.
A tabela já existe realmente e eu não quero tentar criar ela novamente, mas é o Hibernate que está tentando fazer isso.
Quando eu inicio a aplicação o Hibernate não tentar criar novamente as tabelas "normais", porém tenta criar novamente a tabela que eu particionei.
Eu recriei o banco de dados várias vezes para fazer testes e só retorna o erro se eu particionar a tabela.
GOSTEI 0
Yago Vieira
31/05/2023
A questão é o seguinte:
A tabela já existe realmente e eu não quero tentar criar ela novamente, mas é o Hibernate que está tentando fazer isso.
Quando eu inicio a aplicação o Hibernate não tentar criar novamente as tabelas "normais", porém tenta criar novamente a tabela que eu particionei.
Eu recriei o banco de dados várias vezes para fazer testes e só retorna o erro se eu particionar a tabela.
A tabela já existe realmente e eu não quero tentar criar ela novamente, mas é o Hibernate que está tentando fazer isso.
Quando eu inicio a aplicação o Hibernate não tentar criar novamente as tabelas "normais", porém tenta criar novamente a tabela que eu particionei.
Eu recriei o banco de dados várias vezes para fazer testes e só retorna o erro se eu particionar a tabela.
GOSTEI 0
Arthur Heinrich
31/05/2023
O Hibernate é um framework que auxilia na construção dinâmica das queries. Porém, precisa saber utilizá-lo.
No entanto, queria entender o por quê você está particionando a tabela e por quê por data.
O particionamento é uma feature muito restrita. São raros os casos onde é realmente útil.
Particionar por data vai criar uma partição para cada data? Não faz muito sentido.
No entanto, queria entender o por quê você está particionando a tabela e por quê por data.
O particionamento é uma feature muito restrita. São raros os casos onde é realmente útil.
Particionar por data vai criar uma partição para cada data? Não faz muito sentido.
GOSTEI 0
Yago Vieira
31/05/2023
O Hibernate é um framework que auxilia na construção dinâmica das queries. Porém, precisa saber utilizá-lo.
No entanto, queria entender o por quê você está particionando a tabela e por quê por data.
O particionamento é uma feature muito restrita. São raros os casos onde é realmente útil.
Particionar por data vai criar uma partição para cada data? Não faz muito sentido.
No entanto, queria entender o por quê você está particionando a tabela e por quê por data.
O particionamento é uma feature muito restrita. São raros os casos onde é realmente útil.
Particionar por data vai criar uma partição para cada data? Não faz muito sentido.
Resposta:
A ideia é criar particionamentos mensais, exemplo: janeiro, fevereiro, março ...
O principal motivo é poder apagar os dados mais antigos depois de um 1 ano ou mais, sem tem que rodar uma query na tabela principal. Além disso, caso seja possível e eu consiga, futuramente eu gostaria de fazer SELECTs direto em uma tabela particionada, exemplo: quero buscar os dados somente de hoje, então vou fazer uma consulta na tabela de junho, para não precisar filtrar na tabela principal, onde terá muitos dados.
Eu consegui resolver o problema que mencionei, o que fiz foi atualizar o Spring Boot para a última versão e adicionar a seguinte dependência no pom.xml:
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>6.2.3.Final</version> </dependency>
Mas se tiver uma forma mais adequada de lidar com esse caso, sem usar particionamento, eu gostaria de ouvir sugestões.
GOSTEI 0
Arthur Heinrich
31/05/2023
Eu não conheço o PostgreSQL a fundo, para saber como funciona o particionamento dele. Pelo que pude ver, até o momento, não existem "global indexes". Então, a estratégia de dropar partições, ao invés de excluir dados, parece ser uma alternativa viável.
Porém, você precisa ter em mente que, ao não permitir índices globais, sempre que você pensar em filtrar uma determinada característica que não seja a utilizada na partição, o banco terá que fazer múltiplos range scans.
Imagine uma tabela com 5 anos de dados, particionada mês a mês. O banco precisará acessar cada uma das 60 partições e efetuar a busca no índice.
Agora, imagine um join entre duas tabelas particionadas. Se a tabela A e B possuem ambas 60 partições, ao fazer um join, o banco vai acessar cada uma das partições da tabela A e, para fazer o join com a tabela B, terá que acessar todas as partições da tabela B, totalizando 3600 combinações.
Agora que ficou claro que você pode estar adicionando uma dificuldade a mais, para obter um bom desempenho nas queries, que tal pensar em uma estratégia que não utilize partições?
Ao invés de purgar 1 mês inteiro, durante a virada, porque não purgar 1 dia por vez, durante a madrugada, ou mesmo purgar 1 hora de dados, de hora em hora?
Porém, você precisa ter em mente que, ao não permitir índices globais, sempre que você pensar em filtrar uma determinada característica que não seja a utilizada na partição, o banco terá que fazer múltiplos range scans.
Imagine uma tabela com 5 anos de dados, particionada mês a mês. O banco precisará acessar cada uma das 60 partições e efetuar a busca no índice.
Agora, imagine um join entre duas tabelas particionadas. Se a tabela A e B possuem ambas 60 partições, ao fazer um join, o banco vai acessar cada uma das partições da tabela A e, para fazer o join com a tabela B, terá que acessar todas as partições da tabela B, totalizando 3600 combinações.
Agora que ficou claro que você pode estar adicionando uma dificuldade a mais, para obter um bom desempenho nas queries, que tal pensar em uma estratégia que não utilize partições?
Ao invés de purgar 1 mês inteiro, durante a virada, porque não purgar 1 dia por vez, durante a madrugada, ou mesmo purgar 1 hora de dados, de hora em hora?
GOSTEI 0