无法使用 Spring-Boot 创建多个数据源
Posted
技术标签:
【中文标题】无法使用 Spring-Boot 创建多个数据源【英文标题】:Unable to create multiple datasources with Spring-Boot 【发布时间】:2017-04-17 12:22:51 【问题描述】:tomcat 出现以下错误:
09:06:18,168 WARNING [org.apache.tomcat.jdbc.pool.PooledConnection] (ServerService Thread Pool -- 6) Not loading a JDBC driver as driverClassName property is null.
09:06:18,170 SEVERE [org.apache.tomcat.jdbc.pool.ConnectionPool] (ServerService Thread Pool -- 6) Unable to create initial connections of pool.: java.sql.SQLException: The url cannot be null
我在 JBOSS 和嵌入式 tomcat 服务器中都运行过这个,但仍然出现这个错误。我什至从war中取出jar文件并从JBOSS运行它,仍然得到同样的错误。
我能够创建 EntityMangers,但在创建它们之前我得到了上述错误。该程序继续运行而不是抱怨一个类不是托管类型。但是那些@Entities
正在被扫描。
我在使用 JBOSS 时遇到同样的错误:
09:06:18,171 WARN [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator] (ServerService Thread Pool -- 6) HHH000342: Could not obtain connection to query metadata : The url cannot be null
09:06:18,183 INFO [org.hibernate.dialect.Dialect] (ServerService Thread Pool -- 6) HHH000400: Using dialect: org.hibernate.dialect.Oracle10gDialect
09:06:18,199 INFO [org.hibernate.engine.jdbc.env.internal.LobCreatorBuilderImpl] (ServerService Thread Pool -- 6) HHH000422: Disabling contextual LOB creation as connection was null
数据源:
@Configuration
@PropertySource("classpath:application.yml")
public class MainDataSourceConfig
/*******************************
* Datasource *
* *****************************/
@Bean
@Primary
@ConfigurationProperties(prefix = "spring.dataSource.Main")
public DataSource mainDataSource()
return DataSourceBuilder.create().build();
/*******************************
* Transaction manager *
* *****************************/
@Bean
@Primary
DataSourceTransactionManager transactionManager(@Qualifier("mainDataSource") DataSource datasource)
DataSourceTransactionManager txm = new DataSourceTransactionManager(datasource);
return txm;
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean entityManagerFactory(@Qualifier("mainDataSource")DataSource dataSource)
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setPersistenceUnitName("mainEntityManger");
entityManagerFactory.setDataSource(dataSource);
entityManagerFactory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
entityManagerFactory.setJpaDialect(new HibernateJpaDialect());
entityManagerFactory.setPackagesToScan("com.customers.domain");
entityManagerFactory.setJpaProperties(additionalProperties());
return entityManagerFactory;
private Properties additionalProperties()
Properties properties = new Properties();
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.Oracle10gDialect");
properties.setProperty("hibernate.ddl-auto","none");
return properties;
第二个数据源
@Configuration
@PropertySource("classpath:application.yml")
public class SecondDataSourceConfig
/*******************************
* Datasource *
* *****************************/
@Bean
@ConfigurationProperties(prefix="spring.dataSource.Second")
public DataSource secondDataSource()
return DataSourceBuilder.create().build();
/*******************************
* Transaction manager *
* *****************************/
@Bean
DataSourceTransactionManager transactionManager(@Qualifier("secondDataSource") DataSource datasource)
DataSourceTransactionManager txm = new DataSourceTransactionManager(datasource);
return txm;
@Bean
public LocalContainerEntityManagerFactoryBean keyviewEntityMangerFactory(@Qualifier("secondDataSource") DataSource dataSource)
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setPersistenceUnitName("secondEntityManger");
entityManagerFactory.setDataSource(dataSource);
entityManagerFactory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
entityManagerFactory.setJpaDialect(new HibernateJpaDialect());
entityManagerFactory.setPackagesToScan("com.statements.domain");
entityManagerFactory.setJpaProperties(additionalProperties());
return entityManagerFactory;
private Properties additionalProperties()
Properties properties = new Properties();
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.Oracle10gDialect");
properties.setProperty("hibernate.ddl-auto","none");
return properties;
application.yml
#Spring Boot Config for Oracle
spring:
dataSource:
Main:
url: [url]
username: [username]
password: [password]
driverClassName: oracle.jdbc.OracleDriver
Second:
url: [url]
username: [username]
password: [password]
driverClassName: oracle.jdbc.OracleDriver
tomcat:
min-idle: 1
# Spring Boot Actuator settings
#https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-endpoints.html
endpoints.health.sensitive: false
#management.security.enabled: true
management.context-path: /healthCheck
endpoints.info.id: info
endpoints.info.sensitive: false
endpoints.info.enabled: true
info.app.name: Request
info.app.description: Request Description
info.app.version: 0.0.1-SNAPSHO
我已经做了一个多星期了,我无法弄清楚为什么它说 url 为空,后来创建 entityManagers 时说我的一个域包不是托管类型。扫描第二组软件包似乎有问题。我确认了更改类名,因为我注意到它是按字母顺序编译的,当我这样做时,它改变了它抱怨的包。 #note:每次我运行同一个包时,它都会选择一个不同的类,并且它们是 @Entity
注释的。
我也在 main 方法上使用@EntityScan
、@ComponentScan
、@SpringBootApplication
、@EnableAutoConfiguration
。
注意:当我删除第二个数据源时,一切正常。只有当我引入第二个时才会发生这种情况。
------------更新1--------- --------------
我只是将数据源信息放入两个不同的yml
文件中,但仍然出现相同的错误。然后我决定把主要的数据源拿出来,把第二个作为项目中唯一的一个来实现。 那么我得到了同样的错误。但是 URI 和一切都是正确的,不知道为什么会这样。
【问题讨论】:
【参考方案1】:我最近在使用 spring boot(没有 SpringBootApplication 注释)创建测试工具时遇到了类似的问题,我最终发现我需要使用数据源属性并在配置类中显式指定 @EnableConfigurationProperties:
@Configuration
@EnableConfigurationProperties(value = DataSourceProperties.class)
public class ....
为数据源属性定义的bean:
@Bean(name = "ds1Props")
@ConfigurationProperties(prefix = "path.to.ds1.properties")
public DataSourceProperties ds1DataSourceProperties()
return new DataSourceProperties();
@Bean(name = "ds2Props")
@ConfigurationProperties(prefix = "path.to.ds2.properties")
public DataSourceProperties ds2DataSourceProperties()
return new DataSourceProperties();
然后将它们注入到数据源bean方法中:
@Bean
public DataSource dataSource1(@Qualifier("ds1Props") DataSourceProperties ds1Props)
return ds1Props.initializeDataSourceBuilder().build();
@Bean
public DataSource dataSource2(@Qualifier("ds2Props") DataSourceProperties ds2Props)
return ds2Props.initializeDataSourceBuilder().build();
希望这对您有所帮助或助您一臂之力。我不确定为什么需要将其显式设置为 jdbc 自动配置的一部分。 (我想可能是因为我的数据源配置后可能会处理spring boot数据源配置)
--- 更新:基于原始源的工作示例,修改以匹配我的数据库环境,使用带有 starter web 和 starter jpa 包的 spring boot 1.3.1。 @Primary 需要满足对自动配置的 DataSourceInitializer 和 OpenSessionInViewInterceptor 的依赖关系。
@SpringBootApplication
public class Application
public static void main(String[] args)
SpringApplication.run(new Object[] Application.class , args);
@Primary
@Bean
@ConfigurationProperties(prefix = "spring.dataSource.Main")
public DataSource mainDataSource()
return DataSourceBuilder.create().build();
@Bean
DataSourceTransactionManager mainTransactionManager(@Qualifier("mainDataSource") DataSource datasource)
DataSourceTransactionManager txm = new DataSourceTransactionManager(datasource);
return txm;
@Primary
@Bean
public LocalContainerEntityManagerFactoryBean mainEntityManagerFactory(
@Qualifier("mainDataSource") DataSource dataSource)
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setPersistenceUnitName("mainEntityManger");
entityManagerFactory.setDataSource(dataSource);
entityManagerFactory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
entityManagerFactory.setJpaDialect(new HibernateJpaDialect());
entityManagerFactory.setPackagesToScan("com.sbp.domain.main");
entityManagerFactory.setJpaProperties(additionalProperties());
return entityManagerFactory;
@Bean
@ConfigurationProperties(prefix = "spring.dataSource.Second")
public DataSource secondDataSource()
return DataSourceBuilder.create().build();
@Bean
DataSourceTransactionManager secondTransactionManager(@Qualifier("secondDataSource") DataSource datasource)
DataSourceTransactionManager txm = new DataSourceTransactionManager(datasource);
return txm;
@Bean
public LocalContainerEntityManagerFactoryBean secondEntityMangerFactory(
@Qualifier("secondDataSource") DataSource dataSource)
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setPersistenceUnitName("secondEntityManger");
entityManagerFactory.setDataSource(dataSource);
entityManagerFactory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
entityManagerFactory.setJpaDialect(new HibernateJpaDialect());
entityManagerFactory.setPackagesToScan("com.sbp.domain.second");
entityManagerFactory.setJpaProperties(additionalProperties());
return entityManagerFactory;
private Properties additionalProperties()
Properties properties = new Properties();
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.mysql5Dialect");
properties.setProperty("hibernate.ddl-auto", "none");
return properties;
【讨论】:
我按照你说的做了,事情看起来很积极,但现在它说应用程序无法启动。描述:Cannot determine embedded database driver class for database type NONE
操作:If you want an embedded database please put a supported one on the classpath. If you have database settings to be loaded from a particular profile you may need to active it (no profiles are currently active).
但它只针对第二个数据源说。 Unsatisfied dependency expressed through method 'secondEntityMangerFactory' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException:
能分享一下spring boot版本和你拉进来的boot包吗?我在上面获取了您的原始代码,使用已启动的 web 和 starter jpa 创建了一个项目,并且能够毫无错误地启动应用程序。
also - 这是另一个具有替代实现但细节很好的帖子。 ***.com/questions/26308035/…
这里是我使用您的原始源代码创建的应用程序的完整源代码:【参考方案2】:
您需要设置两组具有唯一名称的 bean(即 transactionManager2)
摆脱@Primary - 说把这个bean放在另一个上面。在这种情况下,您需要上下文中的两个 bean - 因此需要唯一的名称。
见:http://docs.spring.io/spring-boot/docs/current/reference/html/howto-data-access.html
如果您使用的是 Spring Data JPA,请执行以下操作:
@Configuration
@EnableJpaRepositories(basePackageClasses = Customer.class,
entityManagerFactoryRef = "customerEntityManagerFactory")
public class CustomerConfiguration
...
@Configuration
@EnableJpaRepositories(basePackageClasses = Order.class,
entityManagerFactoryRef = "orderEntityManagerFactory")
public class OrderConfiguration
...
【讨论】:
以上是关于无法使用 Spring-Boot 创建多个数据源的主要内容,如果未能解决你的问题,请参考以下文章
无法实现对 Spring-Boot API 的基于角色的访问
使用 Google Cloud Pub/Sub 消息订阅者实现时无法创建 Spring-Boot bean
在春季批处理(spring-boot-1.5.2.RELEASE)中使用多个数据源在启动时引发异常