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.
Related
Does anyone knows to configure a EmbeddedDatabaseBuilder Datasource with Simple-JNDI?
I have a DataSource for testing purposes that I am building like this:
public DataSource dataSource() {
EmbeddedDatabase datasource = new EmbeddedDatabaseBuilder()
.setType(HSQL)
.setSeparator(";")
.addScript("classpath:/tables-definitions.sql")
.build();
return datasource;
}
And I want to bind this to a JNDI name with Simple-JNDI. Do you know how to do this?
Finally I found the answer how to use Simple-JNDI to bind to a jndi name a data source that you already have, in general for testing purposes.
Just an observation if you try to use SimpleNamingContextBuilder:
SimpleNamingContextBuilder is deprecated in spring 5.2 and above in favour of Simple-JNDI
Unfortunately I did not find a great source of documentation for Simple-JNDI which make things for more advanced stuff a bit cumbersome.
Also SimpleNamingContextBuilder does not work with JTA only with JPA - it does not cover all the naming needs for JTA
Now, how to bind a JNDI name with Simple-JNDI to a data source:
you need to set Context.INITIAL_CONTEXT_FACTORY to the factory class that you want to do the job for you, in my case I choose org.osjava.sj.memory.MemoryContextFactory - if you look into the location of this class in Simple-JNDI library, you will find more options there, depending of your needs
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.osjava.sj.memory.MemoryContextFactory");
Then you create a Hashtable and add all the properties you need to set as you set them through properties.
Hashtable env = new Hashtable();
env.put("org.osjava.sj.jndi.shared", "true");
Then you bind the DS to JNDI name as you would do it in old plain way:
create the initial context with the properties from the Hashtable
create the sub-context
then bind the data source to JNDI name/context:
InitialContext ic = new InitialContext(env);
ic.createSubcontext("java:/comp/env/jdbc");
ic.bind("java:/comp/env/jdbc/"+dataSourceJndiname, datasource);
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
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
I have one master db. After login with master db I have some another db. Is it possible to connect at runtime to second db and also have instanace of first db also(master db) application using spring-jdbc or hibernate,
thanks in advance.
Yes, sure. You can create as many data sources as you need. Just define them in the Spring Context and autowire in you classes. This question might help you with defining components with the same type but different names.
UPD1: you can create a datasource at runtime just like that:
DataSource ds = new DataSource();
ds.setUsername("username");
ds.setPassword("password");
ds.setDriverClassName("com.mysql.jdbc.Driver"); // or another driver
ds.setUrl("jdbc:mysql://{hostname}:{port}/{dbName}?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false");
ds.setTestWhileIdle(true);
ds.setTestOnBorrow(true);
ds.setTestOnReturn(false);
ds.setValidationQuery("/* ping */ SELECT 1");
ds.setValidationQueryTimeout(1);
ds.setValidationInterval(30000);
ds.setTimeBetweenEvictionRunsMillis(30000);
ds.setMinIdle(1);
ds.setMaxWait(10000);
ds.setMaxIdle(10);
ds.setInitialSize(10);
ds.setMinEvictableIdleTimeMillis(30000);
I'm trying to follow Sun's JDBC tutorial at http://java.sun.com/docs/books/tutorial/jdbc/basics/connecting.html
It gives the following example code:
DataSource ds = (DataSource) org.apache.derby.jdbc.ClientDataSource()
ds.setPort(1527);
ds.setHost("localhost");
ds.setUser("APP")
ds.setPassword("APP");
Connection con = ds.getConnection();
This code doesn't compile because the DataSource interface has none of these methods, except for the getConnection() method invoked last.
(Here's the javadoc: http://java.sun.com/javase/6/docs/api/javax/sql/DataSource.html)
What am I missing?
Edit:
I'm actually trying to connect to MySQL (com.mysql.jdbc) and I can't find the javadoc for that. I'll accept an answer that points me to either:
1) documentation for com.mysql.jdbc regarding a DataSource that I can understand, or
2) gives an example to follow for what the tutorial's code should be, for any database.
One thing you might want to look at is the Commons DBCP project. It provides a BasicDataSource that is configured fairly similarly to your example. To use that you need the database vendor's JDBC JAR in your classpath and you have to specify the vendor's driver class name and the database URL in the proper format.
Edit:
If you want to configure a BasicDataSource for MySQL, you would do something like this:
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUsername("username");
dataSource.setPassword("password");
dataSource.setUrl("jdbc:mysql://<host>:<port>/<database>");
dataSource.setMaxActive(10);
dataSource.setMaxIdle(5);
dataSource.setInitialSize(5);
dataSource.setValidationQuery("SELECT 1");
Code that needs a DataSource can then use that.
DataSource is vendor-specific, for MySql you could use MysqlDataSource which is provided in the MySql Java connector jar:
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setDatabaseName("xyz");
dataSource.setUser("xyz");
dataSource.setPassword("xyz");
dataSource.setServerName("xyz.yourdomain.com");
Basically in JDBC most of these properties are not configurable in the API like that, rather they depend on implementation. The way JDBC handles this is by allowing the connection URL to be different per vendor.
So what you do is register the driver so that the JDBC system can know what to do with the URL:
DriverManager.registerDriver((Driver) Class.forName("com.mysql.jdbc.Driver").newInstance());
Then you form the URL:
String url = "jdbc:mysql://[host][,failoverhost...][:port]/[database][?propertyName1][=propertyValue1][&propertyName2][=propertyValue2]"
And finally, use it to get a connection:
Connection c = DriverManager.getConnection(url);
In more sophisticated JDBC, you get involved with connection pools and the like, and application servers often have their own way of registering drivers in JNDI and you look up a DataSource from there, and call getConnection on it.
In terms of what properties MySQL supports, see here.
EDIT: One more thought, technically just having a line of code which does Class.forName("com.mysql.jdbc.Driver") should be enough, as the class should have its own static initializer which registers a version, but sometimes a JDBC driver doesn't, so if you aren't sure, there is little harm in registering a second one, it just creates a duplicate object in memeory.
use MYSQL as Example:
1) use database connection pools: for Example: Apache Commons DBCP , also, you need basicDataSource jar package in your classpath
#Bean
public BasicDataSource dataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/gene");
ds.setUsername("root");
ds.setPassword("root");
return ds;
}
2)use JDBC-based Driver it is usually used if you don't consider connection pool:
#Bean
public DataSource dataSource(){
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/gene");
ds.setUsername("root");
ds.setPassword("root");
return ds;
}
I think the example is wrong - javax.sql.DataSource doesn't have these properties either. Your DataSource needs to be of the type org.apache.derby.jdbc.ClientDataSource, which should have those properties.
The javadoc for DataSource you refer to is of the wrong package. You should look at javax.sql.DataSource. As you can see this is an interface. The host and port name configuration depends on the implementation, i.e. the JDBC driver you are using.
I have not checked the Derby javadocs but I suppose the code should compile like this:
ClientDataSource ds = org.apache.derby.jdbc.ClientDataSource()
ds.setHost etc....
For postgres, the below works. I actually used it in integ tests. I guess there should be some more consideration for production usage.
PGSimpleDataSource ds = new PGSimpleDataSource() ;
ds.setServerName( "localhost" );
ds.setDatabaseName( "your_db_name_here" );
ds.setUser( "scott" );
ds.setPassword( "tiger" );
The class is bundled in the postgres jdbc driver.
The original stackoverflow post i followed: https://stackoverflow.com/a/45091982/3877642