Spring Boot - Database Initialization

Estou tentando criar uma forma que, ao iniciar o sistema, ele popule uma tabela e persista no SqlServer, mas tenho que garantir que, se o sistema iniciar novamente, não realize outro insert.

Verifiquei que é possível usando um arquivo com nome data.sql “How-to” Guides (spring.io), mas quando eu uso if no código, o sql retorna:

Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Syntax error in SQL statement "IF[*] (NOT EXISTS(SELECT * FROM USUARIO U WHERE U.NOME='Moderador'))"; SQL statement:
IF (not exists(select * from USUARIO u where u.nome='Moderador'))

O sql eu testei direto no BD e funcionou.

IF (not exists(select * from USUARIO u where u.nome='Admin'))
begin
	INSERT INTO USUARIO(nome, login , senha) VALUES('Admin', 'admin', 'senhahash');
end

Aparentemente tu está usando H2!
SQL Server e H2 tem diferenças de sintaxe, por isso é acusado erro.

Para isso, acho que seria melhor tu usar ferramentas como flyway ou liquibase. Qualquer uma dessas vai ser uma mão na roda pra tu! Muito bom usar elas em projetos para manter o banco de dados.

no SQL Server deu erro também
IF (NOT EXISTS(SELECT * FROM eaas_cloud.dbo.[user] u where u.name=‘admin’))
begin
INSERT INTO eaas_cloud.dbo.[user] (active, login, name, creation_date, password, email ) VALUES( 1 ,‘admin’, ‘admin’,CURRENT_TIMESTAMP, ‘senhahash’,‘admin@admin.com’)
end

org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘dataSourceScriptDatabaseInitializer’ defined in class path resource [org/springframework/boot/autoconfigure/sql/init/DataSourceInitializationConfiguration.class]: Invocation of init method failed; nested exception is org.springframework.jdbc.datasource.init.ScriptStatementFailedException: Failed to execute SQL script statement #2 of URL [file:/C:/Users/Suporte/Documents/JEE%20WorkSpace/eaas-CLOUD/eaas-cloud-api/target/classes/data.sql]: IF (NOT EXISTS(SELECT * FROM eaas_cloud.dbo.[user] u where u.name=‘admin’)); nested exception is com.microsoft.sqlserver.jdbc.SQLServerException: Incorrect syntax near ‘)’.
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1804) ~[spring-beans-5.3.14.jar:5.3.14]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620) ~[spring-beans-5.3.14.jar:5.3.14]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.14.jar:5.3.14]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.14.jar:5.3.14]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.14.jar:5.3.14]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.14.jar:5.3.14]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.14.jar:5.3.14]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322) ~[spring-beans-5.3.14.jar:5.3.14]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.14.jar:5.3.14]
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1154) ~[spring-context-5.3.14.jar:5.3.14]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:908) ~[spring-context-5.3.14.jar:5.3.14]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.14.jar:5.3.14]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) ~[spring-boot-2.6.2.jar:2.6.2]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730) ~[spring-boot-2.6.2.jar:2.6.2]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:412) ~[spring-boot-2.6.2.jar:2.6.2]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:302) ~[spring-boot-2.6.2.jar:2.6.2]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301) ~[spring-boot-2.6.2.jar:2.6.2]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1290) ~[spring-boot-2.6.2.jar:2.6.2]
at com.dnia.eaascloud.EaasCloudApiApplication.main(EaasCloudApiApplication.java:11) ~[classes/:na]
Caused by: org.springframework.jdbc.datasource.init.ScriptStatementFailedException: Failed to execute SQL script statement #2 of URL [file:/C:/Users/Suporte/Documents/JEE%20WorkSpace/eaas-CLOUD/cloud-api/target/classes/data.sql]: IF (NOT EXISTS(SELECT * FROM eaas_cloud.dbo.[user] u where u.name=‘admin’)); nested exception is com.microsoft.sqlserver.jdbc.SQLServerException: Incorrect syntax near ‘)’.

Vou tentar com flyway

O flyway serve exatamente para controlar esse tipo de coisa (criar tabelas, executar cargas iniciais, etc). E vc mantém todos os scripts na própria aplicação. Porém, ele necessita que seja criada uma tabela no schema da aplicação para que ele guarde esse controle (o que já foi executado, o que deu certo ou não).

Se me lembro bem, há restrições no que vc pode executar no SQL. Por exemplo, no que vc precisa executar, acho que teria que ser apenas:

INSERT INTO USUARIO 
  (nome, login , senha) 
VALUES
  ('Admin', 'admin', 'senhahash');

Isso seria executa no momento em que a aplicação fosse iniciada apenas uma única vez.

Se eu reiniciar então ele saberia que que o insert já foi realizado?

Sim, o controle de schema evolution dele só aplicaria a migration uma única vez no DB.

O controle pode ser observado pela tabela flyway_schema_version, que é a tabela que o flyway usa para controlar as migrations já aplicadas de não aplicadas e inclusive migrations modificadas pós execução (isso causaria um erro).

2 curtidas

Obrigado