Spring Boot auto reconnect to PostgreSQL using JdbcTemplate and multiple datasources - spring

I have a Spring Boot v 1.5.1.RELEASE app which uses PostgreSQL 9.6 as a datasource. My app remains connected to Postgres even when idle, but if the connection is lost then the app does not reconnect and instead throws:
org.springframework.jdbc.support.MetaDataAccessException: Error while extracting DatabaseMetaData; nested exception is org.postgresql.util.PSQLException: This connection has been closed.
at org.springframework.jdbc.support.JdbcUtils.extractDatabaseMetaData(JdbcUtils.java:342) ~[spring-jdbc-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.jdbc.support.JdbcUtils.extractDatabaseMetaData(JdbcUtils.java:366) ~[spring-jdbc-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.jdbc.support.SQLErrorCodesFactory.getErrorCodes(SQLErrorCodesFactory.java:212) ~[spring-jdbc-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.setDataSource(SQLErrorCodeSQLExceptionTranslator.java:134) [spring-jdbc-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.<init>(SQLErrorCodeSQLExceptionTranslator.java:97) [spring-jdbc-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.jdbc.support.JdbcAccessor.getExceptionTranslator(JdbcAccessor.java:99) [spring-jdbc-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:649) [spring-jdbc-4.3.6.RELEASE.jar:4.3.6.RELEASE]
Although I am not using JPA and I believe I am using the Tomcat pool that comes with spring-boot-starter, I have read and tried the suggestions discussed here and here with no luck. In my properties file, I have tried:
#spring.datasource.tomcat.test-on-borrow=true
#spring.datasource.tomcat.validation-query=SELECT 1
#spring.datasource.tomcat.test-while-idle=true
#spring.datasource.tomcat.time-between-eviction-runs-millis=3600000
#spring.datasource.tomcat.validation-query=SELECT 1
#spring.datasource.dbcp2.test-on-borrow=true
#spring.datasource.dbcp2.validation-query=SELECT 1
spring.datasource.test-on-borrow=true
spring.datasource.validation-query=SELECT 1
However, I am using two data sources, configured like this:
#Configuration
public class DatabaseConfiguration
{
#Bean
#Primary
#ConfigurationProperties(prefix = "spring.ds_pgsql_rtmain")
public DataSource rtmainDataSource()
{
DataSource dataSource = DataSourceBuilder.create().build();
return dataSource;
}
#Bean
#ConfigurationProperties(prefix = "spring.ds_pgsql_pdns")
public DataSource pdnsDataSource()
{
return DataSourceBuilder.create().build();
}
#Bean
#Primary
public JdbcTemplate rtmainJdbcTemplate(DataSource rtmainDataSource)
{
return new JdbcTemplate(rtmainDataSource);
}
#Bean
public JdbcTemplate pdnsJdbcTemplate(#Qualifier("pdnsDataSource") DataSource pdnsDataSource) {
return new JdbcTemplate(pdnsDataSource);
}
}
I'm not sure if the problem is that I did not configure the data pool correctly or because the pool does not work when I manually configure the data sources. Or something else. Assistance would be greatly appreciated, thank you.

Yes since you're not using an auto configured data source it is not working.
Since you're applying properties from your own prefix you just have to put test-on-borrow and validation-query on your own prefix. Try this:
spring.ds_pgsql_rtmain.test-on-borrow=true
spring.ds_pgsql_rtmain.validation-query=SELECT 1
spring.ds_pgsql_pdns.test-on-borrow=true
spring.ds_pgsql_pdns.validation-query=SELECT 1

Related

How can I create a db2 connection in Spring boot without use application.properties?

I need to build a connection to db2 database, but I can not use application.properties at this moment, so how can I create this connection?
You can implement a datasource bean in the app itself. For example:
#Configuration
public class MyClass {
#Bean
public DataSource getDataSource() {
DataSourceBuilder<?> dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.driverClassName(JDBC_DRIVER_CLASS_NAME);
dataSourceBuilder.url(myprops.getJDBCUrl());
dataSourceBuilder.username(myprops.getJDBCUsername());
dataSourceBuilder.password(myprops.getJDBCPassword());
return dataSourceBuilder.build();
}
}
Then, make sure any autowired references to the data source are "lazy", so the app has time to load the properties (maybe from another file) and to instantiate the data source, as in:
#Lazy
#Autowired
private DataSource dataSource;

How to prevent HSQLDB from persisting data between applications runs when using Spring Boot?

I am run a Spring Boot/Web application and I am using an embedded HSQLDB during prototyping. As part of this I do not want to persist data between runs. I would like the only data in the system to be that from those scripts.
How can I modify my configuration to achieve that?
#Configuration
public class RepoConfig {
#Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase db = builder.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:table.sql")
.addScript("classpath:data.sql")
.build()
;
return db;
}
#Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
return jdbcTemplate;
}
}
I tried adding a script to drop the tables before creating them, like
DROP TABLE mytablename ;
but I kept getting the error:
user lacks privilege or object not found: MYTABLENAME
PS:
Spring Boot 2.5.0
HSQLDB 2.6.0
You can do that with the following configuration:
spring.jpa.hibernate.ddl-auto = create-drop
You can find more detail on Initialize a Database Using Hibernate
Or also you can set this property spring.datasource.data
spring.datasource.data=drop.sql,data.sql
then those files should be in
src/main/resources/drop.sql
src/main/resources/data.sql

Spring Boot - How to check number of active connections in the connection pool

I am using Spring Boot 1.5.17 with multiple data sources configured. One of my data sources is configured as follows.
#Primary
#Bean
#ConfigurationProperties("app.primary.datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
Now how do I get or log the number of active connections in the connection pool for this data source?
It's a bit late , but DataSource interface doesn't have any methods that lets you do this, but if you are using HikariCP (which is the default).
You could do this.
(HikariDataSource)dataSource).getHikariPoolMXBean().getActiveConnections()
Solution (Kotlin code):
#Autowired var dataSource: DataSource
val hikaridataSource = dataSource as HikariDataSource
logger.info(
active: ${hikaridataSource.hikariPoolMXBean.activeConnections} " + "idle: ${hikaridataSource.hikariPoolMXBean.idleConnections} " + "threadsAwaitingConnection: ${hikaridataSource.hikariPoolMXBean.threadsAwaitingConnection}"
)

Warning: Not loading a JDBC driver as driverClassName property is null, in springboot working with two datasources

I have currently configured spring boot to work with two different datasources. The application is working fine, however when I start the spring boot application I get an warning repeated 10 times like below:
2018-06-05 10:28:15.897 WARN 8496 --- [r://myScheduler] o.a.tomcat.jdbc.pool.PooledConnection : Not loading a JDBC driver as driverClassName property is null.
As I mentioned this is not affecting my application, but I would like to know why I am having this kind of warning and if there is any way to fix it.
When using two or more datasources you need to configure them yourself. In that case Spring Boot's DataSourceAutoConfiguration (and also DataSourceProperties) won't be used.
You most probably have the related DB details like the name of the JDBC driver class name in application.properties file as follows:
spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
primary.datasource.url=jdbc:sqlserver://xxx.yyy.zzz.www:1433;databaseName=primaryDB
primary.datasource.username=username
primary.datasource.password=password
other.datasource.url=jdbc:sqlserver://xxx.yyy.zzz.www:1433;databaseName=otherDb
other.datasource.username=otheruser
other.datasource.password=otherpassword
Thus, to set the driver class name for the datasource just say:
#Value("${spring.datasource.driver-class-name}")
String driverClassName;
#Primary
#Bean(name = "primaryDb")
#ConfigurationProperties(prefix = "primary.datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().driverClassName(driverClassName).build();
}
#Bean(name = "otherDb")
#ConfigurationProperties(prefix = "other.datasource")
public DataSource otherDataSource() {
return DataSourceBuilder.create().driverClassName(driverClassName).build();
}

SpringBoot manage connection pool errors

I have a spring-boot application with 3 webservices that access to two different databases declared in application.properties file.
spring.datasource.url = jdbc:oracle
spring.datasource.username = aa
spring.datasource.password = aa
spring.seconddatasource.url = jdbc:oracle2
spring.seconddatasource.username = aa
spring.seconddatasource.password = aa
When I run the application, if a connection fails it ends the whole application even if one of the connections works.
I need to connect to all databases, and if a database isn't working, try to reconnect, but the application can not end.
I have tried these configurations but with no success :
testOnBorrow=true
validationQuery=SELECT 1
timeBetweenEvictionRunsMillis = 60000
Also I have a DataBaseConfig.java with
#Configuration
public class DataBaseConfig {
#Bean(name = "mysqlDb")
#Primary
#ConfigurationProperties(prefix="spring.datasource")
public DataSource mysqlDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "sqliteDb")
#ConfigurationProperties(prefix="spring.secondDatasource")
public DataSource sqliteDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "cli")
public JdbcTemplate slaveJdbcTemplate(#Qualifier("mysqlDb") DataSource datasource) {
return new JdbcTemplate(datasource);
}
#Bean(name = "usr")
#Primary
public JdbcTemplate masterJdbcTemplate(#Qualifier("sqliteDb") DataSource secondDatasource) {
return new JdbcTemplate(secondDatasource);
}
}
Console errors :
Unable to create initial connections of pool
HHH000342: Could not obtain connection to query metadata : ORA-01034: ORACLE not available
ORA-27101: shared memory realm does not exist
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.service.spi.ServiceException: Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]
Create the connection pools programmatically rather than letting Spring Boot autoconfigure them for you. Then you can handle any errors in building the datasource in your code. See:
Configure DataSource programmatically in Spring Boot
Alternatively create a single connection datasource at runtime rather than at boot time, and add retry logic in the event of an error (have a look at Spring Retry)

Resources