Problema envolvendo tabela particionada + Spring Boot: GenerationTarget encountered exception accepting command : Error executing DDL
31/05/2023
0
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
Posts
31/05/2023
Frank Hosaka
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?
31/05/2023
Yago Vieira
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.
31/05/2023
Yago Vieira
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.
01/06/2023
Arthur Heinrich
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.
05/06/2023
Yago Vieira
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.
05/06/2023
Arthur Heinrich
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?
Clique aqui para fazer login e interagir na Comunidade :)