In my Spring boot(2.0.7 RELEASE) application I am not able to manually set/override the timeout for the database connections in the application.properites file. I am using JPA, Hibernate, Tomcat connection pool and Postgres.
I've researched thoroughly and found very similar questions :
Overriding timeout for database connection in properties file
JPA query timeout parameters ignored but #Transaction annotation works
The reason I ask new question is because neither of the questions above have an accepted answer nor a confirmed working solution. I tried including each proposed solution in my application.properties file with no success.
Also, as mentioned in question 2: if I add parameter 'timeout = someSeconds' in the #Transactional annotation, the connection timeouts as expected but if I try extracting it in the application.properties it fails and timeouts for the default time. The problem here is that I want all connections to timeout in the given time not only the transactions.
Things I've tried in the application.properties (The desired timeout is 4 seconds):
spring.jpa.properties.javax.persistence.query.timeout=4000
spring.jdbc.template.query-timeout=4
spring.transaction.defaultTimeout=4
spring.datasource.tomcat.validation-query-timeout=4
Materials I've read:
http://www.masterspringboot.com/configuration/web-server/configuring-tomcat-connection-pool-on-spring-boot
https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html
https://www.baeldung.com/spring-boot-tomcat-connection-pool
https://www.objectdb.com/java/jpa/query/setting#Query_Hints_
Am I missing some property? Does anyone know why the timeout can't be overridden via the application.properties file?
Thanks in advance.
There are at least 3 time-outs to configure:
Transaction timeouts, which you already did. I declared mine in the transactionManager bean:
txManager.setDefaultTimeout(myDefaultValue);
Query timeouts(which obviously does not need #transactional), which you already did and also explained here
Network timeouts(Read this excellent article).
For my case, i am using Oracle, and my bean configuration is as follows:
#Bean
public HikariDataSource dataSource() {
HikariDataSource ds = new HikariDataSource();
ds.setDriverClassName(springDatasourceDriverClassName);
ds.setJdbcUrl(springDatasourceUrl);
ds.setUsername(springDatasourceUsername);
ds.setPassword(springDatasourcePassword);
ds.setDataSourceProperties(oracleProperties());
return ds;
}
Properties oracleProperties() {
Properties properties = new Properties();
properties.put("oracle.net.CONNECT_TIMEOUT", 10000);
properties.put("oracle.net.READ_TIMEOUT", 10000);
properties.put("oracle.jdbc.ReadTimeout", 10000);
return properties;
}
And if you do not want to configure a bean for the DataSource(which is what most people will do), you can configure the network timeout properties in application.properties:
spring.datasource.hikari.data-source-properties.oracle.net.CONNECT_TIMEOUT=10000
spring.datasource.hikari.data-source-properties.oracle.net.READ_TIMEOUT=10000
spring.datasource.hikari.data-source-properties.oracle.jdbc.ReadTimeout=10000
Depending on your datasource, but you can try this:
spring.datasource.hikari.max-lifetime=1000
spring.datasource.hikari.connection-timeout=1000
spring.datasource.hikari.validation-timeout=1000
spring.datasource.hikari.maximum-pool-size=10
Related
I have a question about springboot, quartz scheduler and HikariCP. I am relatively new to this domain and trying to understand the relations and working.
I have gone through many questions that are either related to Springboot HikariCP or Quartz scheduler using HikariCP but none of them is able to answer my questions.
I have an application with below configurations
#Database properties
spring.datasource.url = jdbc:mysql://localhost:3306/demo?user=root&password=root&useSSL=false&serverTimezone=UTC
spring.datasource.username = root
spring.datasource.password = root
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
#Hikari
spring.datasource.hikari.minimumIdle=5
spring.datasource.hikari.maximumPoolSize=20
#quartz settings
spring.quartz.properties.org.quartz.jobStore.dataSource = quartzDataSource
spring.quartz.properties.org.quartz.dataSource.quartzDataSource.driver = com.mysql.cj.jdbc.Driver
spring.quartz.properties.org.quartz.dataSource.quartzDataSource.provider=hikaricp
spring.quartz.properties.org.quartz.dataSource.quartzDataSource.URL = jdbc:mysql://localhost:3306/demo?user=root&password=root&useSSL=false&serverTimezone=UTC
spring.quartz.properties.org.quartz.dataSource.quartzDataSource.user = root
spring.quartz.properties.org.quartz.dataSource.quartzDataSource.password = root
spring.quartz.job-store-type = jdbc
spring.quartz.properties.org.quartz.threadPool.threadCount=20
By default, springboot2 uses HikariCP. I have set the pool size to 20.
In quartz scheduler too, I have set it to use HikariCP.
Now my questions are
Whether springboot and quartz using the same connection pool or quartz is creating a new pool?
If quartz is creating a new pool, Is there any way to configure both such that both uses same connection pool created by springboot.
What should be the optimal connection pool for 1k,10k,50k users?
Thanks in advance.
Sorry, don't have enough time to come back with a complete answer, but maybe this will help:
you're giving the connection details in 2 different places, it's safe to assume you're creating 2 datasources with different pools.
Found this: https://www.candidjava.com/tutorial/quartz-reuse-existing-data-source-connection-pool/
The number of users can't be directly correlated to connection pool size. You should look at the number of concurrent requests you want to support: for 100 req/sec, each req taking 100 ms -> you need 10 connections. This is a very simplified way of calculating but it's a starting point, after that: monitoring and adjusting should help you.
Reusing Spring's datasource in Quartz is possible, and has been the case since Spring framework 4.x.
By default, Quartz creates a new connection pool based on the provided data source properties.
Even if you instruct Quartz to use a connection pooling provider (since it supports c3p0 and HikariCP out of the box), it will still create a new connection pool using the providers. It all comes down to the implementation details of the Quartz's JobStoreCMT class, which is usually the JobStore implementation used in Spring applications by default. JobStoreCMT will always create it's own pool.
Reusing Spring's datasource in Quartz is however very trivial, using the SchedulerFactoryBean in Spring. It accepts a Spring managed datasource through the setDataSource, as shown in the following snippet
#Configuration
public class SchedulerConfig {
#Autowired private DataSource dataSource;
#Bean
public SchedulerFactoryBean schedulerFactoryBean(){
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setDataSource(dataSource);
// ... set other properties
return factory;
}
Internally, Spring Framework instructs Quartz to use LocalDataSourceJobStore (a Spring provided job store that extends Quartz's JobStoreCMT) to manage jobs, when a datasource is provided to SchedulerFactoryBean. LocalDataSourceJobStore has a custom Quartz connection provider that reuses the provided datasource, instead of creating a new connection.
In Spring Boot 2, this is even simpler, since it does all of the auto-wiring, to use the application's default data source. One only needs to configure Quartz to use a JDBC store type:
spring.quartz.job-store-type=jdbc
Configuring Quartz to use a data source again in the properties file, might interfere with this autowiring behavior, and result in creation of a Quartz managed datasource with a new connection pool.
I am looking for the right way to set a run-time parameter when a database connection is open. My run-time parameter is actually a time zone, but I think this should work for an arbitrary parameter.
I've found following solutions, but I feel like none of these is the right thing.
JdbcInterceptor
Because Spring Boot has Apache Tomcat connection pool as default I can use org.apache.tomcat.jdbc.pool.JdbcInterceptor to intercept connections.
I don't think this interceptor provides a reliable way to perform a statement when connection is open. Possibility to intercept every statement provided by this interceptor is unnecessary to set a parameter that should be set only once.
initSQL property
Apache's pooled connection has a build-in ability to initialise itself by a statement provided by PoolProperties.initSQL parameter. This is executed in ConnectionPool.createConnection(...) method.
Unfortunately official support for this parameter has been removed from Spring and no equivalent functionality has been introduced since then.
I mean, I can still use a datasource builder like in an example below, and then hack the property into a connection pool, but this is not a good looking solution.
// Thank's to property binders used while creating custom datasource,
// the datasource.initSQL parameter will be passed to an underlying connection pool.
#Bean
#ConfigurationProperties(prefix = "datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
Update
I was testing this in a Spring Boot 1.x application. Above statements are no more valid for Spring Boot 2 applications, because:
Default Tomcat datasource was replaced by Hikari which supports spring.datasource.hikari.connection-init-sql property. It's documentation says Get the SQL string that will be executed on all new connections when they are created, before they are added to the pool.
It seems that similar property was reintroduced for Tomcat datasource as spring.datasource.tomcat.init-s-q-l.
ConnectionPreparer & AOP
This is not an actual solution. It is more like an inspiration. The connection preparer was a mechanism used to initialise Oracle connections in Spring Data JDBC Extensions project. This thing has its own problems and is no more maintained but possibly can be used as a base for similar solution.
If your parameter is actually a time zone, why don't you find a way to set this property.
For example if you want to store or read a DateTime with a predefined timestamp the right way to do this is to set property hibernate.jdbc.time_zone in hibernate entityManager or spring.jpa.properties.hibernate.jdbc.time_zone in application.properties
Spring Boot : How to add new Datasource at runtime
My project want to connect two datasource.
The first datasource I can Config in application.properties but the second datasource can't config because this config is in the tableConfig from DB of the first datasource.
So,
config the 1st datasource.
query data from the 1st datasource for get config of 2nd datasource (url, username, password).
add new 2nd datasource
Now, I config two Datasource from application.properties and it's work.
But the requirement want to change the 2nd datasource from table of 1st datasource. T.T
Please, gives me some suggestions.
Thank you.
A Spring configuration like this should work (consider it pseudo code):
#Bean("secondDatasource")
public Datasource secondDatasource(#Qualifier("firstDatasource") Datasource ds){
// use `ds` to obtain the necessary information to obtain a datasource ...
return DataSourceBuilder
.create()
.username(username)
.password(pwd)
.url(url)
.driverClassName(driver)
.build();
}
I would at least start without using Spring Data JPA in the configuration class and operate directly on the data source to keep things simple.
You already got pointers, how to set up Spring Data JPA to then use the different data sources: http://www.baeldung.com/spring-data-jpa-multiple-databases
The code above is mainly just copied from: https://stackoverflow.com/a/28822145
I have multitenant database in Spring Boot. I store multi spring JDBC templates (based on tomcat Data Sources, configured manually) in map (immutable bean). And I choose proper data source based on uuid in a request (connection pool per database). I have disabled standard configuration in Spring Boot by:
#SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
What is the proper way of transaction manager configuration? With single data source I can use PlatformTransactionManager, but how it should be done with multiple jdbc templates/data sources in spring? It would be the best if I could set everything dynamically. Thanks in advance.
Here a solution for using multiple datasources
http://www.baeldung.com/spring-data-jpa-multiple-databases
Configure Two DataSources
If you need to configure multiple data sources, you can apply the same tricks that are described in the previous section. You must, however, mark one of the DataSource #Primary as various auto-configurations down the road expect to be able to get one by type.
If you create your own DataSource, the auto-configuration will back off. In the example below, we provide the exact same features set than what the auto-configuration provides on the primary data source
#Bean
#Primary
#ConfigurationProperties("app.datasource.foo")
public DataSourceProperties fooDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#Primary
#ConfigurationProperties("app.datasource.foo")
public DataSource fooDataSource() {
return fooDataSourceProperties().initializeDataSourceBuilder().build();
}
#Bean
#ConfigurationProperties("app.datasource.bar")
public BasicDataSource barDataSource() {
return (BasicDataSource) DataSourceBuilder.create()
.type(BasicDataSource.class).build();
}
fooDataSourceProperties has to be flagged #Primary so that the database initializer feature uses your copy (should you use that).
app.datasource.foo.type=com.zaxxer.hikari.HikariDataSource
app.datasource.foo.maximum-pool-size=30
app.datasource.bar.url=jdbc:mysql://localhost/test
app.datasource.bar.username=dbuser
app.datasource.bar.password=dbpass
app.datasource.bar.max-total=30
So, after searching Google and Github for answers, I'm left confounded as to how most people know how to use HikariCP. I can't seem to find any straight up documentation on HikariCP.
My question is: how do I specify the host address without a JDBC URL? The main page of HikariCP on Github clearly says that JDBC URL specification is optional and instead to simply use HikariConfig#setDataSourceClassName(String). However, I'm confused then as to how I would specify my address and I can't seem to find the answer anywhere. Like with SQLite, where do I specify the path to where the database file should go?
This is the code I currently have:
final HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setPoolName("SQLite");
hikariConfig.setDataSourceClassName("org.sqlite.SQLiteDataSource");
HikariDataSource ds = new HikariDataSource(hikariConfig);
If I was to not use HikariCP I would simply specify the JDBC URL like such:
jdbc:sqlite:path/to/database.db. However, how do you do this without using a JDBC URL?
Thanks for any help.
When you use DataSource-style instead of URL-style configuration, all datasource properties translate to setters on the DataSource class. So, since you seem to be using the org.sqlite.SQLiteDataSource, the various setters on that class are what is relevant.
Here is an example of setting not only the URL of the DataSource, but also setting the Journal Mode and enabling full column name support.
final HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setPoolName("SQLite");
hikariConfig.setDataSourceClassName("org.sqlite.SQLiteDataSource");
hikariConfig.addDataSourceProperty("url", "jdbc:sqlite:C:/work/mydatabase.db");
hikariConfig.addDataSourceProperty("journalMode", "WAL");
hikariConfig.addDataSourceProperty("fullColumnNames", "true");
HikariDataSource ds = new HikariDataSource(hikariConfig);
This reason DataSource-style is preferable is two-fold:
The use of reflection ensures that any typo in a property name causes a failure. Whereas, properties with typos specified in the JDBC URL itself are typically ignored by drivers.
When there are a large number of properties specified, a JDBC URL connection string can get extremely long -- several hundred characters for some drivers -- which makes reading/understanding what properties are actually set extremely cumbersome.