多数据源回退
Multiple Datasources fallback
根据建议更新:
我有一个 Springboot Java 应用程序和两个在 application.properties 中定义的数据源:
spring.jpa.hibernate.ddl-auto=update
#online
spring.datasource.url=jdbc:mysql://172.17.0.2:3306/gcdb?allowPublicKeyRetrieval=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driverClassName=com.mysql.jdbc.Driver
#offline
spring.secondDatasource.url=jdbc:h2:mem:gcdboff
spring.secondDatasource.username=root
spring.secondDatasource.password=root
spring.secondDatasource.driverClassName=org.h2.Driver
# Enabling H2 Console
spring.h2.console.enabled=true
spring.h2.console.path=/h2
手动将凭据从 "datasource" 更改为 "secondDatasource" 后,两个连接都正常工作。
但现在我想添加一个后备。如果"datasource"不可用,请取"secondDatasource".
我找到了以下解决方案。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
@SpringBootApplication
public class GCApplication {
public static void main(String[] args) {
SpringApplication.run(GCApplication.class, args);
}
/**/
@Bean
@Primary
@ConfigurationProperties(prefix="spring.datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix="spring.secondDatasource")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
/**/
}
但现在没有人会运行。该应用程序以以下错误结束,我不知道如何更正该错误。
2018-07-06 09:52:30.805 ERROR 28073 --- [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Unsatisfied dependency expressed through method 'entityManagerFactory' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'entityManagerFactoryBuilder' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Unsatisfied dependency expressed through method 'entityManagerFactoryBuilder' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaVendorAdapter' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.orm.jpa.JpaVendorAdapter]: Factory method 'jpaVendorAdapter' threw exception; nested exception is java.lang.IllegalArgumentException: dataSource or dataSourceClassName or jdbcUrl is required.
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:732) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
[...]
感谢您的帮助!
嗯,异常很简单,说:
java.lang.IllegalArgumentException: dataSource or dataSourceClassName or jdbcUrl is required
所以这意味着您缺少其中一个数据源的配置 属性。
如果我们查看您的属性文件,我们可以看到 spring.datasource.driverClassName
配置 属性。
因此您缺少 MySQL
数据库的 driverClassName
,您需要指定它。
我找到了解决问题的方法。我希望它也能帮助你。使用以下代码,在第一个连接失败后选择第二个数据连接:
在 GC 应用程序中:
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
@SpringBootApplication
public class GCApplication
{
public static void main(String[] args)
{
SpringApplication.run(GCApplication.class, args);
}
@Primary
@Bean
public DataSource getDataSource(
@Qualifier("first") DataSourceProperties first,
@Qualifier("second") DataSourceProperties second)
{
int i = 0;
final DataSource firstDataSource = first.initializeDataSourceBuilder().build();
final DataSource secondDataSource = second.initializeDataSourceBuilder().build();
try
{
firstDataSource.getConnection();
return firstDataSource;
}
catch (Exception e)
{
return secondDataSource;
}
}
@Primary
@Bean("first")
@ConfigurationProperties(prefix = "spring.datasource")
public DataSourceProperties primaryDataSource()
{
final DataSourceProperties dataSourceProperties = new DataSourceProperties();
dataSourceProperties.setUrl("jdbc:mysql://172.17.0.2:3306/gcdb?allowPublicKeyRetrieval=true&useSSL=false");
dataSourceProperties.setDriverClassName("com.mysql.jdbc.Driver");
dataSourceProperties.setUsername("root");
dataSourceProperties.setPassword("root");
return dataSourceProperties;
}
@Bean("second")
public DataSourceProperties secondaryDataSource() {
final DataSourceProperties dataSourceProperties = new DataSourceProperties();
dataSourceProperties.setUrl("jdbc:h2:mem:gcdboff");
dataSourceProperties.setDriverClassName("org.h2.Driver");
dataSourceProperties.setUsername("root");
dataSourceProperties.setPassword("root");
return dataSourceProperties;
}
}
在application.properties中:
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database=default
# Enabling H2 Console
spring.h2.console.enabled=true
spring.h2.console.path=/h2
只是为了添加到@Tagamoga 的回答中,您可以将三个 bean:getDataSource、primaryDataSource 和 secondaryDataSource 添加到一个单独的 class 中,并使用 @Configuration 对其进行注释。在那种情况下,您有一个单独的配置,您仍然可以使用属性文件来配置数据库连接。
像这样:
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.jdbc.DataSourceInitializationMode;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
@Configuration
public class DatabaseConfig {
@Value("${datasource.primary.url}")
String primaryDSUrl;
@Value("${datasource.primary.platform}")
String primaryDSPlatform;
@Value("${datasource.primary.username}")
String primaryDSUsername;
@Value("${datasource.primary.password}")
String primaryDSPassword;
@Value("${datasource.secondary.url}")
String secondaryDSUrl;
@Value("${datasource.secondary.platform}")
String secondaryDSPlatform;
@Value("${datasource.secondary.username}")
String secondaryDSUsername;
@Value("${datasource.secondary.password}")
String secondaryDSPassword;
@Primary
@Bean
public DataSource getDataSource(
@Qualifier("first") DataSourceProperties first,
@Qualifier("second") DataSourceProperties second) {
final DataSource firstDataSource = first.initializeDataSourceBuilder().build();
final DataSource secondDataSource = second.initializeDataSourceBuilder().build();
try {
firstDataSource.getConnection();
return firstDataSource;
} catch (Exception e) {
return secondDataSource;
}
}
@Primary
@Bean("first")
public DataSourceProperties primaryDataSource() {
final DataSourceProperties dataSourceProperties = new DataSourceProperties();
dataSourceProperties.setUrl(primaryDSUrl);
dataSourceProperties.setPlatform(primaryDSPlatform);
dataSourceProperties.setUsername(primaryDSUsername);
dataSourceProperties.setPassword(primaryDSPassword);
dataSourceProperties.setInitializationMode(DataSourceInitializationMode.ALWAYS);
return dataSourceProperties;
}
@Bean("second")
public DataSourceProperties secondaryDataSource() {
final DataSourceProperties dataSourceProperties = new DataSourceProperties();
dataSourceProperties.setUrl(secondaryDSUrl);
dataSourceProperties.setPlatform(secondaryDSPlatform);
dataSourceProperties.setUsername(secondaryDSUsername);
dataSourceProperties.setPassword(secondaryDSPassword);
dataSourceProperties.setInitializationMode(DataSourceInitializationMode.ALWAYS);
return dataSourceProperties;
}
}
和属性文件:
#Datasource Primary
datasource.primary.platform=oracle
datasource.primary.url=jdbc:oracle:thin:@localhost:1521:orcl
datasource.primary.username=LCO2TL
datasource.primary.password=LCO2TL2020
#Datasource Secondary
datasource.secondary.platform=h2
datasource.secondary.url=jdbc:h2:mem:test"
datasource.secondary.username=root
datasource.secondary.password=root
根据建议更新:
我有一个 Springboot Java 应用程序和两个在 application.properties 中定义的数据源:
spring.jpa.hibernate.ddl-auto=update
#online
spring.datasource.url=jdbc:mysql://172.17.0.2:3306/gcdb?allowPublicKeyRetrieval=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driverClassName=com.mysql.jdbc.Driver
#offline
spring.secondDatasource.url=jdbc:h2:mem:gcdboff
spring.secondDatasource.username=root
spring.secondDatasource.password=root
spring.secondDatasource.driverClassName=org.h2.Driver
# Enabling H2 Console
spring.h2.console.enabled=true
spring.h2.console.path=/h2
手动将凭据从 "datasource" 更改为 "secondDatasource" 后,两个连接都正常工作。
但现在我想添加一个后备。如果"datasource"不可用,请取"secondDatasource".
我找到了以下解决方案。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
@SpringBootApplication
public class GCApplication {
public static void main(String[] args) {
SpringApplication.run(GCApplication.class, args);
}
/**/
@Bean
@Primary
@ConfigurationProperties(prefix="spring.datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix="spring.secondDatasource")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
/**/
}
但现在没有人会运行。该应用程序以以下错误结束,我不知道如何更正该错误。
2018-07-06 09:52:30.805 ERROR 28073 --- [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Unsatisfied dependency expressed through method 'entityManagerFactory' parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'entityManagerFactoryBuilder' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Unsatisfied dependency expressed through method 'entityManagerFactoryBuilder' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jpaVendorAdapter' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.orm.jpa.JpaVendorAdapter]: Factory method 'jpaVendorAdapter' threw exception; nested exception is java.lang.IllegalArgumentException: dataSource or dataSourceClassName or jdbcUrl is required.
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:732) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE]
[...]
感谢您的帮助!
嗯,异常很简单,说:
java.lang.IllegalArgumentException: dataSource or dataSourceClassName or jdbcUrl is required
所以这意味着您缺少其中一个数据源的配置 属性。
如果我们查看您的属性文件,我们可以看到 spring.datasource.driverClassName
配置 属性。
因此您缺少 MySQL
数据库的 driverClassName
,您需要指定它。
我找到了解决问题的方法。我希望它也能帮助你。使用以下代码,在第一个连接失败后选择第二个数据连接:
在 GC 应用程序中:
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
@SpringBootApplication
public class GCApplication
{
public static void main(String[] args)
{
SpringApplication.run(GCApplication.class, args);
}
@Primary
@Bean
public DataSource getDataSource(
@Qualifier("first") DataSourceProperties first,
@Qualifier("second") DataSourceProperties second)
{
int i = 0;
final DataSource firstDataSource = first.initializeDataSourceBuilder().build();
final DataSource secondDataSource = second.initializeDataSourceBuilder().build();
try
{
firstDataSource.getConnection();
return firstDataSource;
}
catch (Exception e)
{
return secondDataSource;
}
}
@Primary
@Bean("first")
@ConfigurationProperties(prefix = "spring.datasource")
public DataSourceProperties primaryDataSource()
{
final DataSourceProperties dataSourceProperties = new DataSourceProperties();
dataSourceProperties.setUrl("jdbc:mysql://172.17.0.2:3306/gcdb?allowPublicKeyRetrieval=true&useSSL=false");
dataSourceProperties.setDriverClassName("com.mysql.jdbc.Driver");
dataSourceProperties.setUsername("root");
dataSourceProperties.setPassword("root");
return dataSourceProperties;
}
@Bean("second")
public DataSourceProperties secondaryDataSource() {
final DataSourceProperties dataSourceProperties = new DataSourceProperties();
dataSourceProperties.setUrl("jdbc:h2:mem:gcdboff");
dataSourceProperties.setDriverClassName("org.h2.Driver");
dataSourceProperties.setUsername("root");
dataSourceProperties.setPassword("root");
return dataSourceProperties;
}
}
在application.properties中:
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database=default
# Enabling H2 Console
spring.h2.console.enabled=true
spring.h2.console.path=/h2
只是为了添加到@Tagamoga 的回答中,您可以将三个 bean:getDataSource、primaryDataSource 和 secondaryDataSource 添加到一个单独的 class 中,并使用 @Configuration 对其进行注释。在那种情况下,您有一个单独的配置,您仍然可以使用属性文件来配置数据库连接。 像这样:
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.jdbc.DataSourceInitializationMode;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
@Configuration
public class DatabaseConfig {
@Value("${datasource.primary.url}")
String primaryDSUrl;
@Value("${datasource.primary.platform}")
String primaryDSPlatform;
@Value("${datasource.primary.username}")
String primaryDSUsername;
@Value("${datasource.primary.password}")
String primaryDSPassword;
@Value("${datasource.secondary.url}")
String secondaryDSUrl;
@Value("${datasource.secondary.platform}")
String secondaryDSPlatform;
@Value("${datasource.secondary.username}")
String secondaryDSUsername;
@Value("${datasource.secondary.password}")
String secondaryDSPassword;
@Primary
@Bean
public DataSource getDataSource(
@Qualifier("first") DataSourceProperties first,
@Qualifier("second") DataSourceProperties second) {
final DataSource firstDataSource = first.initializeDataSourceBuilder().build();
final DataSource secondDataSource = second.initializeDataSourceBuilder().build();
try {
firstDataSource.getConnection();
return firstDataSource;
} catch (Exception e) {
return secondDataSource;
}
}
@Primary
@Bean("first")
public DataSourceProperties primaryDataSource() {
final DataSourceProperties dataSourceProperties = new DataSourceProperties();
dataSourceProperties.setUrl(primaryDSUrl);
dataSourceProperties.setPlatform(primaryDSPlatform);
dataSourceProperties.setUsername(primaryDSUsername);
dataSourceProperties.setPassword(primaryDSPassword);
dataSourceProperties.setInitializationMode(DataSourceInitializationMode.ALWAYS);
return dataSourceProperties;
}
@Bean("second")
public DataSourceProperties secondaryDataSource() {
final DataSourceProperties dataSourceProperties = new DataSourceProperties();
dataSourceProperties.setUrl(secondaryDSUrl);
dataSourceProperties.setPlatform(secondaryDSPlatform);
dataSourceProperties.setUsername(secondaryDSUsername);
dataSourceProperties.setPassword(secondaryDSPassword);
dataSourceProperties.setInitializationMode(DataSourceInitializationMode.ALWAYS);
return dataSourceProperties;
}
}
和属性文件:
#Datasource Primary
datasource.primary.platform=oracle
datasource.primary.url=jdbc:oracle:thin:@localhost:1521:orcl
datasource.primary.username=LCO2TL
datasource.primary.password=LCO2TL2020
#Datasource Secondary
datasource.secondary.platform=h2
datasource.secondary.url=jdbc:h2:mem:test"
datasource.secondary.username=root
datasource.secondary.password=root