Reputation: 15
This is my migration
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.8.xsd">
<changeSet id="1" author="svetliosa">
<createTable tableName="users">
<column name="id" type="BIGINT" autoIncrement="true">
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="username" type="VARCHAR(255)">
<constraints unique="true" nullable="false"/>
</column>
<column name="password" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
<column name="created_at" type="TIMESTAMP" defaultValueComputed="CURRENT_TIMESTAMP"/>
</createTable>
</changeSet>
</databaseChangeLog>
and this is mt db.changelog-master.xml
<databaseChangeLog>
<include file="db/changelog/db.test.xml"/>
</databaseChangeLog>
This is my entity
package ecommerce_app.ecom;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.time.LocalDateTime;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "username", nullable = false, unique = true)
private String username;
@Column(name = "password", nullable = false)
private String password;
@Column(name = "created_at", nullable = false, updatable = false)
private LocalDateTime createdAt;
// Constructors
public User() {}
public User(String username, String password) {
this.username = username;
this.password = password;
}
// Getters and Setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
}
and this is my Liquibase configuration file
package ecommerce_app.ecom;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import liquibase.integration.spring.SpringLiquibase;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import jakarta.annotation.PostConstruct;
import javax.sql.DataSource;
@Configuration
public class LiquibaseConfig {
@Bean
@Primary
public DataSource dataSource() {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setJdbcUrl("jdbc:h2:mem:ecomdb");
hikariConfig.setUsername("sa");
hikariConfig.setPassword("k6oS3aLoR4tbL3wsgRfEXRIdb6GzSulMcg6adLV6TOQ");
hikariConfig.setDriverClassName("org.h2.Driver");
hikariConfig.setMaximumPoolSize(10);
hikariConfig.setMinimumIdle(5);
return new HikariDataSource(hikariConfig);
}
@Bean
public SpringLiquibase liquibase(DataSource dataSource) {
SpringLiquibase liquibase = new SpringLiquibase();
liquibase.setDataSource(dataSource);
liquibase.setChangeLog("classpath:db/changelog/db.changelog-master.xml");
return liquibase;
}
@PostConstruct
public void initLiquibase() {
SpringLiquibase liquibase = liquibase(dataSource());
try {
liquibase.afterPropertiesSet();
} catch (Exception e) {
e.printStackTrace();
}
}
}
this is my pom.xml file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>ecommerce-app</groupId>
<artifactId>ecom</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>ecom</name>
<description>Ecommerce project</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>true</scope>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<version>4.25.1</version>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.0</version> <!-- or latest version -->
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
And the error is
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Failed to initialize dependency 'liquibase' of LoadTimeWeaverAware bean 'entityManagerFactory': Error creating bean with name 'liquibase' defined in class path resource [ecommerce_app/ecom/LiquibaseConfig.class]: Circular depends-on relationship between 'liquibase' and 'entityManagerFactory'
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:328) ~[spring-beans-6.2.3.jar:6.2.3]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:207) ~[spring-beans-6.2.3.jar:6.2.3]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:970) ~[spring-context-6.2.3.jar:6.2.3]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:627) ~[spring-context-6.2.3.jar:6.2.3]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.4.3.jar:3.4.3]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) ~[spring-boot-3.4.3.jar:3.4.3]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) ~[spring-boot-3.4.3.jar:3.4.3]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) ~[spring-boot-3.4.3.jar:3.4.3]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) ~[spring-boot-3.4.3.jar:3.4.3]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) ~[spring-boot-3.4.3.jar:3.4.3]
at ecommerce_app.ecom.EcomApplication.main(EcomApplication.java:10) ~[classes/:na]
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'liquibase' defined in class path resource [ecommerce_app/ecom/LiquibaseConfig.class]: Circular depends-on relationship between 'liquibase' and 'entityManagerFactory'
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:310) ~[spring-beans-6.2.3.jar:6.2.3]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-6.2.3.jar:6.2.3]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315) ~[spring-beans-6.2.3.jar:6.2.3]
... 10 common frames omitted
I try to fixed it via chat gpt, but the suggestion does not work for me. I have experience with liquibase migrations, but not with the setup. Please help
Upvotes: 0
Views: 45
Reputation: 5428
The DataSource bean and the initLiquibase call are causing the circular dependency and must be removed.
Use the built-in spring application.yml to define the default DataSource with the following properties:
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource # possibly n/a
url: jdbc:h2:mem:ecomdb
username: sa
password: k6oS3aLoR4tbL3wsgRfEXRIdb6GzSulMcg6adLV6TOQ
hikari:
poolName: Hikari
auto-commit: false
SB will inject this autoconfigured DataSource into your liquibase bean.
If that does not fix it try the following - jHipster generated and works for me.
Replace your LiquibaseConfig class completely with the following - this is generated by jHipster. You will have to point to your own changelog base file: classpath:db/changelog/db.changelog-master.xml
import java.util.concurrent.Executor;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseDataSource;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.env.Profiles;
import liquibase.integration.spring.SpringLiquibase;
import tech.jhipster.config.JHipsterConstants;
import tech.jhipster.config.liquibase.SpringLiquibaseUtil;
@Configuration
public class LiquibaseConfiguration {
private final Logger log = LoggerFactory.getLogger(LiquibaseConfiguration.class);
private final Environment env;
public LiquibaseConfiguration(Environment env) {
this.env = env;
}
@Bean
public SpringLiquibase liquibase(
@Qualifier("taskExecutor") Executor executor,
@LiquibaseDataSource ObjectProvider<DataSource> liquibaseDataSource,
LiquibaseProperties liquibaseProperties,
ObjectProvider<DataSource> dataSource,
DataSourceProperties dataSourceProperties
) {
// If you don't want Liquibase to start asynchronously, substitute by this:
// SpringLiquibase liquibase = SpringLiquibaseUtil.createSpringLiquibase(liquibaseDataSource.getIfAvailable(), liquibaseProperties, dataSource.getIfUnique(), dataSourceProperties);
SpringLiquibase liquibase = SpringLiquibaseUtil.createAsyncSpringLiquibase(
this.env,
executor,
liquibaseDataSource.getIfAvailable(),
liquibaseProperties,
dataSource.getIfUnique(),
dataSourceProperties
);
liquibase.setChangeLog("classpath:config/liquibase/master.xml");
liquibase.setContexts(liquibaseProperties.getContexts());
liquibase.setDefaultSchema(liquibaseProperties.getDefaultSchema());
liquibase.setLiquibaseSchema(liquibaseProperties.getLiquibaseSchema());
liquibase.setLiquibaseTablespace(liquibaseProperties.getLiquibaseTablespace());
liquibase.setDatabaseChangeLogLockTable(liquibaseProperties.getDatabaseChangeLogLockTable());
liquibase.setDatabaseChangeLogTable(liquibaseProperties.getDatabaseChangeLogTable());
liquibase.setDropFirst(liquibaseProperties.isDropFirst());
liquibase.setLabels(liquibaseProperties.getLabels());
liquibase.setChangeLogParameters(liquibaseProperties.getParameters());
liquibase.setRollbackFile(liquibaseProperties.getRollbackFile());
liquibase.setTestRollbackOnUpdate(liquibaseProperties.isTestRollbackOnUpdate());
if (env.acceptsProfiles(Profiles.of(JHipsterConstants.SPRING_PROFILE_NO_LIQUIBASE))) {
liquibase.setShouldRun(false);
} else {
liquibase.setShouldRun(liquibaseProperties.isEnabled());
log.debug("Configuring Liquibase");
}
return liquibase;
}
}
You will need the following dependency:
<dependency>
<groupId>tech.jhipster</groupId>
<artifactId>jhipster-framework</artifactId>
<version>7.9.3</version>
</dependency>
And application.yml spring.datasource as above.
Upvotes: 0
Reputation: 378
When using Spring Boot and Liquibase, you don't need to have a configuration class for liquibase. You can set all properties via application.[yaml|properties]
https://contribute.liquibase.com/extensions-integrations/directory/integration-docs/springboot/
Upvotes: 0