SpringBoot manage connection pool errors - spring

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)

Related

Spring batch – using two data source in one step

Currently my spring-batch-app is configured to use only one data source (properties file). When running the app spring will pick the default configuration.
spring.datasource.url=jdbc:sqlserver ...
spring.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.jpa.show-sql=true
spring.jpa.generate-ddl=false
spring.jpa.hibernate.ddl-auto=none
spring.jpa.hibernate.naming.physical-
strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.SQLServer2012Dialect
I have a task to create a job which required some data from another database. Basically the step will retrieve data from one data source and write the data to another data source.
For the new data source I have created a bean:
#Bean
public DataSource melDataSource() {
DriverManagerDataSource melDataSource = new DriverManagerDataSource();
melDataSource.setDriverClassName("prestosql....");
melDataSource.setUrl("....");
melDataSource.setUsername("....");
melDataSource.setPassword("....");
return melDataSource;
}
And this is how I am calling the dataSource:
#Autowired
private DataSource dataSource;
#Autowired
private DataSource melDataSource;
When running the program I get the following error:
Error creating bean with name
'org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration': Unsatisfied
dependency expressed through field 'dataSource'; nested exception is
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name
'melDataSource': Requested bean is currently in creation: Is there an unresolvable circular
reference?
How can I add another data source ?
Thank you
You need to move your data source bean declaration in a separate class and import that class in your batch configuration class. Something like:
class DataSourceConfiguration {
#Bean
public DataSource melDataSource() {
DriverManagerDataSource melDataSource = new DriverManagerDataSource();
melDataSource.setDriverClassName("prestosql....");
melDataSource.setUrl("....");
melDataSource.setUsername("....");
melDataSource.setPassword("....");
return melDataSource;
}
}
#Configuration
#Import(DataSourceConfiguration.class)
class BatchConfiguration {
// use datasource bean here
}

Spring MVC installation

I would like to create instalation process for the first run of my Spring MVC app. Something like wordpress has (when you first run it, you need to specify DB connection and your first admin account... etc.
I have tried it with spring but the spring won't start because when DataSource Bean is not connected, it will simply fail to start. It always fail when transaction manager bean is beeing created:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'txManager' defined in SpringWebConfig: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Property 'sessionFactory' is required|#]
Is there any way how to start the spring/hibernate app without transaction manager and then load it on-the-fly when user configures his db access details ? I know there is way to do it with application.properties but I want to create a simple install process just like in wordpress so it is most convenient for non-tech users.
EDIT 1: My current code:
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = {"test"}) //package scan
public class SpringWebConfig implements WebMvcConfigurer {
#Bean(name = "dataSource")
public BasicDataSource dataSource() {
if (Config.getInstance().isInstalled()) {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
ds.setUrl("jdbc:mysql://" + Config.getInstance().getDburl() + "/" + Config.getInstance().getDbname());
ds.setUsername(Config.getInstance().getDbuser());
ds.setPassword(Config.getInstance().getDbpass());
this.ds = ds;
}
return ds;
}
#Bean
public HibernateTransactionManager txManager() {
if (sessionFactory() == null) {
return new HibernateTransactionManager();
}
else return new HibernateTransactionManager(sessionFactory());
}
#Bean
public SessionFactory sessionFactory() {
try {
LocalSessionFactoryBuilder builder = new LocalSessionFactoryBuilder(dataSource());
builder.scanPackages(dbEntity).addProperties(getHibernateProperties());
return builder.buildSessionFactory();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
Adding #Lazy to all 3 #Beans solves the issue and hibernate is not requiered at the startup of Spring MVC (configured by class that implements WebMvcConfigurer), this allows to display install page for the user so he can define DB access details.

Am trying to do simple JDBC call in a spring boot application is not working

I am looking forward to creating a service using spring boot application where I like to use JDBC prepared statement call which executes the stored procedure get me the required result.
I like to have connection pooling but unfortunately, I don’t know implement
Summary
(services using spring boot --->Simple JDBC with connection pooling---->Mysql)
For this, I have tried to create a data source and execute jdbc statement but not working
#Controller
public class ExampleController {
#Autowired
private ExampleRepository repo;
#RequestMapping("/")
public #ResponseBody String getDataBaseData() throws SQLException{
return repo.getDataBaseData();
}
}
#Configuration
public class DataSources {
#Bean(name = "primary")
#Primary
#ConfigurationProperties(prefix="spring.datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
}
#Component
public class ExampleRepository {
#Autowired
private DataSource ds;
public String getDataBaseData() throws SQLException {
Connection con = ds.getConnection();
System.out.println(con);
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("select * from emp");
while (rs.next())
System.out.println(rs.getInt(1) + " " + rs.getString(2) + " " + rs.getString(3));
con.close();
return rs.toString();
}
}
getting errors like below
com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could not create connection to database server.
com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could not create connection to database server.
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.HibernateException: Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set
Expected Result : Database data should display in web browser
This is my github repo https://github.com/PradeepKumarHE/SpringBootWithSimpleJDBC/tree/master
where i have DBscript file to create
What I can see from your pom.xml, you are using spring-boot-starter-data-jpa. It fetches unneccessary dependencies, which triggers SpringBoot jpa autoconfiguration.
If you want to use pure jdbc with spring boot, replace spring-boot-starter-data-jpa with spring-boot-starter-jdbc (https://mvnrepository.com/artifact/org.springframework.boot/)
In this case you need to
have mysql jdbc driver declared in maven dependencies
define spring.datasource.url, spring.datasource.username, spring.datasource.password in your properties or yaml (you don't need to define spring.datasource.driver if you have only one jdbc driver in your maven deps)
remove your DataSources configuration, since springboot will autoconfigure it for you

Spring Boot auto reconnect to PostgreSQL using JdbcTemplate and multiple datasources

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

Spring boot - Issue with multiple DataSource

I have a ReST service that needs to fetch data from two different DBs (Oracle and MySQL) and merge this data in the response.
I have below configuration.
Config for DB 1:
#Configuration
public class DbConfig_DB1{
#Bean(name="siebelDataSource")
public EmbeddedDatabase siebelDataSource(){
return new EmbeddedDatabaseBuilder().
setType(EmbeddedDatabaseType.H2).
addScript("schema.sql").
addScript("test-data.sql").
build();
}
#Autowired
#Qualifier("siebelDataSource")
#Bean(name = "siebelJdbcTemplate")
public JdbcTemplate siebelJdbcTemplate(DataSource siebelDataSource) {
return new JdbcTemplate(siebelDataSource);
}
}
Config for DB2:
#Configuration
public class DbConfig_DB2{
#Bean(name="brmDataSource")
public EmbeddedDatabase brmDataSource(){
return new EmbeddedDatabaseBuilder().
setType(EmbeddedDatabaseType.H2).
addScript("schema-1.sql").
addScript("test-data-1.sql").
build();
}
#Autowired
#Qualifier("brmDataSource")
#Bean(name = "brmJdbcTemplate")
public JdbcTemplate brmJdbcTemplate(DataSource brmDataSource) {
return new JdbcTemplate(brmDataSource);
}
}
Data Access:
#Repository
public class SiebelDataAccess {
protected final Logger log = LoggerFactory.getLogger(getClass());
#Autowired
#Qualifier("siebelJdbcTemplate")
protected JdbcTemplate jdbc;
public String getEmpName(Integer id) {
System.out.println(jdbc.queryForObject("select count(*) from employee", Integer.class));
Object[] parameters = new Object[] { id };
String name = jdbc.queryForObject(
"select name from employee where id = ?", parameters,
String.class);
return name;
}
}
I am not able to start the app as I below error:
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private javax.sql.DataSource org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration.dataSource;
nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] is defined: expected single matching bean but found 2: brmDataSource,siebelDataSource
The issue is with two DataSource beans in the context. How to resolve this?
You could mark one of them as #Primary so Spring Boot auto configuration for transactions manager would know which one to pick. If you need to manage transactions with both of them then I'm afraid you would have to setup transactions management explicitly.
Please refer to the Spring Boot documentation
Creating more than one data source works the same as creating the first one. You might want to mark one of them as #Primary if you are using the default auto-configuration for JDBC or JPA (then that one will be picked up by any #Autowired injections).

Resources