I am trying to do a sample microservice which involves pulling data from multiple databases. And while configuring I am getting the Exception in thread "main" java.lang.IllegalStateException: java.lang.NoSuchMethodError: 'void org.springframework.data.jpa.repository.config.JpaRepositoryConfigExtension.registerLazyIfNotAlreadyRegistered error.
My application.properties file is like the following:
spring.datasource.primary.url=jdbc:postgresql://localhost:5432/firstdbsample
spring.datasource.primary.username=postgres
spring.datasource.primary.password=postgresql
spring.datasource.primary.driver-class-name=org.postgresql.Driver
spring.datasource.secondary.url=jdbc:postgresql://localhost:5432/seconddbsample
spring.datasource.secondary.username=postgres
spring.datasource.secondary.password=postgresql
spring.datasource.secondary.driver-class-name=org.postgresql.Driver
I kept all my model, controller and repository classes separately for separate packages, like the following:
com.multidb.primary.controller
com.multidb.primary.model
com.multidb.primary.repository
com.multidb.secondary.controller
com.multidb.secondary.model
com.multidb.secondary.repository
And created 2 separate configuration class for primary and secondary. I kept in com.multidb.config package with PrimaryConfig.java and SecondaryConfig.java
com.multidb.config.PrimaryConfig.java
com.multidb.config.SecondaryConfig.java
PrimaryConfig.java file:
@Configuration
@EnableJpaRepositories(
basePackages = "com.multidb.primary.repository",
entityManagerFactoryRef = "primaryEntityManagerFactory",
transactionManagerRef = "primaryTransactionManager"
)
public class PrimaryConfig {
@Bean(name = "primaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "primaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(
@Qualifier("primaryDataSource") DataSource dataSource) {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource);
em.setPackagesToScan(new String[] { "com.multidb.primary.model" });
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
return em;
}
@Bean(name = "primaryTransactionManager")
public PlatformTransactionManager primaryTransactionManager(
@Qualifier("primaryEntityManagerFactory") EntityManagerFactory entityManagerFactory)
{
return new JpaTransactionManager(entityManagerFactory);
}
}
SecondaryConfig.java:
@Configuration
@EnableJpaRepositories(
basePackages = "com.multidb.secondary.repository",
entityManagerFactoryRef = "secondaryEntityManagerFactory",
transactionManagerRef = "secondaryTransactionManager"
)
public class SecondaryConfig {
@Bean(name = "secondaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "secondaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(
@Qualifier("secondaryDataSource") DataSource dataSource) {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource);
em.setPackagesToScan(new String[] { "com.multidb.secondary.model" });
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
return em;
}
@Bean(name = "secondaryTransactionManager")
public PlatformTransactionManager secondaryTransactionManager(
@Qualifier("secondaryEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
Error like the following:
Correct the classpath of your application so that it contains a single, compatible version of org.springframework.data.jpa.repository.config.JpaRepositoryConfigExtension Exception in thread "main" java.lang.IllegalStateException: java.lang.NoSuchMethodError: 'void org.springframework.data.jpa.repository.config.JpaRepositoryConfigExtension.registerLazyIfNotAlreadyRegistered(java.util.function.Supplier, org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.String, java.lang.Object)'
Referred tutorials:
https://mookypoo.medium.com/spring-boot-connect-to-multiple-databases-4cd56773fbcc
https://www.baeldung.com/spring-data-jpa-multiple-databases
Spring Boot configure and use two data sources
POM.xml:
<?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.5.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.multidb</groupId>
<artifactId>MultiDb</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>MultiDb</name>
<description>Demo project for Spring Boot</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</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-web</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Update/Modification After Suggested Springboot Version 3.3.3
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.3</version> // changed from 3.5.4 to 3.3.3
<relativePath/> <!-- lookup parent from repository -->
</parent>
Updated Error Message
Caused by: org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment] due to: Unable to determine Dialect without JDBC metadata (please set 'jakarta.persistence.jdbc.url' for common cases or 'hibernate.dialect' when a custom Dialect implementation must be provided)

Can anyone suggest where I went in wrong in implementation or suggest good documentation for reference please?
Unable to determine Dialect without JDBC metadata error, you have to add hibernate.dialect property to your LocalContainerEntityManagerFactoryBean through the setter:em.setJpaPropertyMap(Map.of("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect"));
After that, the error will be gone. Here are your edited full configs:
PrimaryConfig:
import jakarta.persistence.EntityManagerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.util.Map;
@Configuration
@EnableJpaRepositories(
basePackages = "com.multidb.primary.repository",
entityManagerFactoryRef = "primaryEntityManagerFactory",
transactionManagerRef = "primaryTransactionManager"
)
public class PostgresConfig {
@Bean(name = "primaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "primaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(
@Qualifier("primaryDataSource") DataSource dataSource) {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource);
em.setPackagesToScan("com.multidb.primary.model");
em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
em.setJpaPropertyMap(Map.of(
"hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect", // dialect property
"hibernate.hbm2ddl.auto", "update" // if you want to create tables from entities automatically
));
return em;
}
@Bean(name = "primaryTransactionManager")
public PlatformTransactionManager primaryTransactionManager(
@Qualifier("primaryEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
SecondaryConfig
import jakarta.persistence.EntityManagerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.util.Map;
@Configuration
@EnableJpaRepositories(
basePackages = "com.multidb.secondary.repository",
entityManagerFactoryRef = "secondaryEntityManagerFactory",
transactionManagerRef = "secondaryTransactionManager"
)
public class SecondaryConfig {
@Bean(name = "secondaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "secondaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(
@Qualifier("secondaryDataSource") DataSource dataSource) {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource);
em.setPackagesToScan("com.multidb.secondary.model");
em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
em.setJpaPropertyMap(Map.of("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect")); // dialectProperty
return em;
}
@Bean(name = "secondaryTransactionManager")
public PlatformTransactionManager secondaryTransactionManager(
@Qualifier("secondaryEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
jdbcUrl is required with driverClassName.This can be fixed by renaming both url property names to jdbc-url:
spring.datasource.primary.jdbc-url=jdbc:postgresql://localhost:5432/firstdbsample
spring.datasource.primary.username=postgres
spring.datasource.primary.password=postgresql
spring.datasource.primary.driver-class-name=org.postgresql.Driver
spring.datasource.secondary.jdbc-url=jdbc:postgresql://localhost:5432/seconddbsample
spring.datasource.secondary.username=postgres
spring.datasource.secondary.password=postgresql
spring.datasource.secondary.driver-class-name=org.postgresql.Driver
After that I got a running application without errors.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With