我有一个连接到两个独立数据库的Spring Boot应用程序。一切正常(我遵循the docs和a tutorial中的步骤),但为了自定义Tomcat JDBC连接池设置,I had to manually configure it(因为通过定义多个数据源,引导自动-configuration被忽略,Spring Boot不再从application.properties读取特定于tomcat的属性。
当我在配置两个DataSource期间使用调试器时,我发现两个org.apache.tomcat.jdbc.pool.DataSource实例在DataSource.PoolProperties [" name"]条目中具有相同的连接池。 请参阅下面的调试器屏幕截图,每个dataSource()方法都在单独的配置类中配置。请注意,定义了相同的连接池。
但是,从我看到的使用jConsole + tomcat JMX,只有一个连接池,它配置了主数据库详细信息(URL,凭据,见下文)。
由于Spring内部有多层抽象,我很难调试它。我有Eclipse Class Decompiler plugin,我通常用它来查看Spring逻辑,但在这种情况下,数据源的初始化代码在bean注册时发生,而不是在Spring Boot实际使用它们时设置数据来源。
最重要的是,你能帮助我理解:
对于第二个问题,有一点related question,但没有答案。有another question这是误报,another one与Spring有关,而不是Spring Boot,所以请不要将其作为dupe报告。
答案 0 :(得分:2)
这是我必须采取的方法,以便为每个数据源获取单独的池。以下是@user3007501上面指出的要点的实现。
DataSourceBuilder
,而是创建一个org.apache.tomcat.jdbc.pool.DataSource
。这将创建池,并配置连接。
如果需要
Hikari
或Dbcp2
,请使用原始Spring源文件中的createPooledDataSource()
或Hikari
配置节替换下面方法Dbcp2
的内容DataSourceConfiguration.java。下方createPooledDataSource()
的显示内容是从链接文件中的Tomcat.dataSource()
方法窃取的。
tomcat
中的datasource
配置的每个下添加application.yml
配置部分config-name-here.datasource.tomcat
中指定的.tomcat
(请注意application.yml
)属性,而不是{{1} },不带config-name-here.datasource
.tomcat
的配置DataSourceProperties
@Qualifier("name of bean from previous step")
# Primary Datasource spring: datasource: username: your-username-for-ds-1 password: your-password-for-ds-1 driver-class-name: net.sourceforge.jtds.jdbc.Driver tomcat: validation-query: select 1 test-on-borrow: true myotherdatasource: datasource: username: your-username-for-ds-2 password: your-password-for-ds-2 driver-class-name: net.sourceforge.jtds.jdbc.Driver # HERE: make sure you have a tomcat config for your second datasource like below tomcat: validation-query: select 1 test-on-borrow: true
取自Spring项目源中的DataSourceConfiguration.java。
createPooledDataSource()
答案 1 :(得分:1)
DataSource
接口是通过将库与库和JavaEE代码兼容而实现的,作为通用JDBC连接源实际上与DB驱动程序一起使用。Hikari
这样的池作为依赖项并配置spring.datasource.*
参数。 Spring将创建并配置单个池DataSource
,它可以在您的代码中自动装配。 DataSource
,还有另一个故事。 SpringBoot自动配置大量使用@ConditionalOnMissingBean
注释来确定可以应用默认行为的情况。 Spring无法创建两个默认数据源,因为它应该使用哪一个是模糊的。spring-boot-autoconfugire
模块中找到它:只有在上下文中没有这种类型的bean时,Spring才会启动DataSource
初始化逻辑。Bean
。 Spring会注意到你的DataSource
并且不会在内部创建池。这是example。DataSource
autoconfiguration here 答案 2 :(得分:0)
我正在回答我当时的所作所为。如果你找到了更好的解决方案,或者Spring会允许多个连接池,请发一个答案,我会选择你的。
因为Spring会根据我在问题中发布的代码配置只有一个连接池(在tomcat CP上设置 validationQuery 和 validationInterval ),我添加了一个预定的方法来保持我的第二个数据源。
<ion-col
在上面的示例中,testDatabaseConnection()调用Spring Data Repository上的方法
@Scheduled(fixedRate=INTERVAL_IN_MS)
public void scheduledTestDatabaseConnection() {
try {
testDatabaseConnection();
LOGGER.trace("Tested EJBCA DB connection with success");
}
catch (Exception e) {
LOGGER.error("Got an error when refreshing the EJBCA DB connection '{}'", e.getMessage());
}
}
答案 3 :(得分:0)
我已经使用了外部tomcat数据源并对其进行了配置。
1)在/conf/server.xml中创建了3个数据源
<Resource auth="Container" driverClassName="oracle.jdbc.OracleDriver" factory="org.apache.tomcat.jdbc.pool.DataSourceFactory" maxTotal="20" maxIdle="10" maxWaitMillis="-1" name="jdbc/firstDS" password="xxxxx" type="javax.sql.DataSource" url="<url1>" username="user1"/>
<Resource auth="Container" driverClassName="oracle.jdbc.OracleDriver" factory="org.apache.tomcat.jdbc.pool.DataSourceFactory" maxTotal="20" maxIdle="10" maxWaitMillis="-1" name="jdbc/secondDS" password="xxxxx" type="javax.sql.DataSource" url="<url2>" username="user2"/>
<Resource auth="Container" driverClassName="oracle.jdbc.OracleDriver" factory="org.apache.tomcat.jdbc.pool.DataSourceFactory" maxTotal="20" maxIdle="10" maxWaitMillis="-1" name="jdbc/thirdDS" password="xxxxx" type="javax.sql.DataSource" url="<url3>" username="user3"/>
2)在/conf/context.xml中声明相同的数据源
<ResourceLink auth="Container" name="jdbc/firstDS" global="jdbc/firstDS" type="javax.sql.DataSource" />
<ResourceLink auth="Container" name="jdbc/secondDS" global="jdbc/secondDS" type="javax.sql.DataSource" />
<ResourceLink auth="Container" name="jdbc/thirdDS" global="jdbc/thirdDS" type="javax.sql.DataSource" />
3)在springboot属性文件中定义数据源jndi-name
spring.datasource.jndi-name=java:comp/env/jdbc/firstDS
second.datasource.jndi-name=java:comp/env/jdbc/secondDS
third.datasource.jndi-name=java:comp/env/jdbc/thirdDS
4)为所有3个数据库定义spring boot DB配置,并确保将至少1个数据源声明为主数据库。 firstDBConfig.java,secondDBConfig.java,thirdDBConfig.java-只需更改jndi-name属性并创建3个配置类
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory",
transactionManagerRef = "transactionManager", basePackages = {"com.first.application.repo"})
public class FirstDbConfig {
@Autowired
private Environment env;
@Primary
@Bean(name = "dataSource")
public DataSource dataSource() throws NamingException {
return (DataSource) new JndiTemplate().lookup(env.getProperty("spring.datasource.jndi-name"));
}
@Primary
@Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder builder, @Qualifier("dataSource") DataSource dataSource) {
return builder.dataSource(dataSource).packages("com.first.application.domain").persistenceUnit("eamPU")
.build();
}
@Primary
@Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(
@Qualifier("entityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
5)在定义的基本包下编写DAO和实体。所有3个Datasource类都应放在单独的包层次结构中。
答案 4 :(得分:0)
以上所有解决方案似乎有点复杂。在SpringBoot 2.0及更高版本中,我们将hikariCP作为默认的连接管理库。
只需如下所述在application.yml中定义数据源,
datasource-read:
hikari:
jdbc-url: someUrl
username: someUser
password: somePwd
maximumPoolSize: 3
datasource-write:
hikari:
jdbc-url: someUrl
username: someUser
password: somePwd
maximumPoolSize: 3
这样定义您的bean配置,
@Bean(name = "readDataSource")
@ConfigurationProperties(prefix = "spring.datasource-read.hikari")
public DataSource readDataSource() {
return DataSourceBuilder.create().build();
}
@Primary
@Bean(name = "readJdbcTemplate")
public NamedParameterJdbcTemplate readJdbcTemplate(
@Qualifier("readDataSource") DataSource readDataSource) {
return new NamedParameterJdbcTemplate(readDataSource);
}