Spring Boot - Get rid of Hikari Data Source and use H2 - java

I would like to use an H2 database in a Spring Boot application.
This is what I have in application.properties:
spring.datasource.name=testdb
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=whydoesthishavetobe
spring.datasource.password=sodifficult
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
This are the relevant lines in the build.gradle file:
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'
When I check the connection for the H2 database, it's all good.
But despite this, when I POST new entities into the application, I see nothing getting created in the H2 database, and Hikari data source is getting used instead in the background, as I see from the logs.
Why is Spring ignoring the application.properties file and not using the H2 data source? How can I force it to use the H2 data source?
This is Spring Boot 2.3.4.RELEASE.

I guess you have multiple datasources and a datasource other tha H2 is being autowired as default.As Hirakicp is a connection pool not a db.
U need not get rid of Hirakicp rather set h2 datasources as primary
The below solution should work if you have multiple datasources ,
If you have multiple data source beans configured, it's just that spring is autowiring other data source to be used as a default source.
using #Primary annotation while declaring H2 Datasource bean should solve this.
Using this annotation will force spring to autowire the datult to h2 datasource.
in case you have not declared a datasource bean for H2 but have other datasource beans, you will need to declare the h2 bean and set it as primary using #primary annotation.
note - Hirakicp is a db connection object not a db .
Example -
#Bean("one")
public BasicDataSource dataSourceOne() {
BasicDataSource basicDataSource = new BasicDataSource();
basicDataSource.setUrl(env.getProperty("spring.a.connectionUrl"));
basicDataSource.setDriverClassName(env.getProperty("spring.a.DriverClass"));
basicDataSource.setUsername(env.getProperty("spring.a.username"));
basicDataSource.setPassword(env.getProperty("spring.a.password"));
// basicDataSource.setMaxActive(2);
return basicDataSource;
}
#Primary
#Bean("two")
public BasicDataSource dataSourceTwo() {
BasicDataSource basicDataSource = new BasicDataSource();
basicDataSource.setUrl(env.getProperty("spring.u.connectionUrl"));
basicDataSource.setDriverClassName(env.getProperty("spring.u.DriverClass"));
basicDataSource.setUsername(env.getProperty("spring.u.username"));
basicDataSource.setPassword(env.getProperty("spring.u.password"));
return basicDataSource;
}

I think this is happening because SpringBoot application creates its own in-memory embedded database while your database client creates its own in-memory embedded database.
I suggest you to change the database from in-memory to filesystem and see if the application and your database client shows the same data.
spring.datasource.url=jdbc:h2:file:/data/sample/testdb
You only need to change the database url to make it a file based database.
If you need to proceed with in-memory approach, you can connect to that via tcp so that both your application and client can use the same database.
spring.datasource.url=jdbc:h2:tcp://localhost/mem:testdb

What do you mean by Why is Spring ignoring the application.properties file and not using the H2 data source? Hikari is connection pool, not a datasource. If you would like to provide your own datasource, you have to inject a bean with the configuration of your interest.
Connection pool A connection pool is a cache of database connections maintained so that the connections can be reused when future requests to the database are required.
Datasource Is where data that is being used to run a report or gain information is originating from.
Check here for more on connection pools: https://www.baeldung.com/java-connection-pooling
But, if it the logging that you don't like, you can set the logger to error with org.zaxxer.hikari=error

Related

Spring boot connection pool understanding

In Spring boot application.properties file we have the following options:
server.tomcat.max-threads = 100
server.tomcat.max-connections = 100
spring.datasource.tomcat.max-active = 100
spring.datasource.tomcat.max-idle = 30
This is my repository class
public interface UserRepository extends JpaRepository<Users,Integer>{}
This is the service class
#Service
#Transactional(rollbackFor = Exception.class)
public class UserService {
#Autowired
private UserRepository userRepository;
public User getUserById(Integer id){return userRepository.findOne(id)}
The question is, how userRepository create the connection to DB and will it use the connection pool from my application properties file. I come from JDBC and hibernate where I used DataManager, DataSource, Connection classes to use the connection pool, but in spring boot I didn't have any line of code with this classes and everything work fine
It works as it worked before but with Spring Boot, Spring makes more tasks for you.
With or without Spring, DAO class as UserRepository doesn't manipulate directly the datasource and doesn't create directly the JDBC connections either.
These are manipulated by the EntityManagerFactory implementation you are using.
With Spring-Hibernate, you still had to configure the EntityManagerFactory.
Now with Spring Boot, you don't need to configure it.
It is done for you.
The new thing of Spring Boot is that you can also configure the server datasource properties now :
server.tomcat.max-threads = 100
server.tomcat.max-connections = 100
spring.datasource.tomcat.max-active = 100
spring.datasource.tomcat.max-idle = 30
as the Tomcat server can be started by the Spring Boot application itself.
This part of the Spring Boot documentation gives the preference order of the datasource implementation :
Production database connections can also be auto-configured using a
pooling DataSource. Here’s the algorithm for choosing a specific
implementation:
We prefer the Tomcat pooling DataSource for its performance and
concurrency, so if that is available we always choose it.
Otherwise, if HikariCP is available we will use it.
If neither the Tomcat pooling datasource nor HikariCP are available
and if Commons DBCP is available we will use it, but we don’t
recommend it in production and its support is deprecated.
Lastly, if Commons DBCP2 is available we will use it.
Update:
As per Spring Boot 2.x, HikariCP is the default connection pool mechanism.

Dynamic datasource as second datasource in Spring Boot + Hibernate

I have a question about how to handle two different datasources in a Spring Boot application.
Use case:
I have one main repository (db) which must be connected all the time
(application scope), i have no problem with that, having
TransactionManager and EntityManager.
The second database connection should be only request scoped, with
dynamic credentials gathered from an httpRequest.
The datasources are both from PostgreSQL.
Is that even possible? If yes, what is the best way to achieve that.
Thanks for help.
This is an interesting twist on the two datasource pattern!
Your second datasource will have to be resolved based on information external to your application, so you won't be able to use the Spring application context.
You can configure a datasource programmatically in Spring, which is covered in this Q&A:
Configure DataSource programmatically in Spring Boot
Your case is slightly different since the credentials will be resolved at runtime, but can use the same idea.
Make sure you have the Spring JDBC dependency.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
Using a DataSourceBuilder, construct a new DataSource object using your credentials
public DataSource getDataSource(String user, String password) {
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.url(DBB_URL);
dataSourceBuilder.username(user);
dataSourceBuilder.password(password);
return dataSourceBuilder.build();
}
And then use that as the datasource for your code. You won't be able to Autowire it, so you'll have come up with a rational strategy for when to construct your datasource in your JDBC methods and use it with a Spring JdbcTemplate in a way that allows you the transaction integrity you are looking for.
There are other considerations that you haven't specified. This strategy assumes you are using the same JDBC driver for both data sources. If you are using different databases, you will have to add the dependencies for those libraries, and then specify the driver class for your dynamic datasource.

Spring Boot Enable Specific Embedded Database HSQL over Derby

I'm trying to deploy My application on weblogic server, My application has an in-memory DB and since I use HSQLDB for JUNIT, I want to keep HSQL as my in-memory DB(primarily a slight better performance over derby). Since weblogic has already derby, when I try to deploy the application, The derby is getting started rather than HSQL. On Preliminary investigation I find that Since Derby is define above HSQL in EmbeddedDatabaseConnection.java, Derby database is being started first. Is there any specific configuration, where I can explicitly embedded database type to HSQL rather than allowing spring boot to start database based on library/classes
First try I can think of is to remove Derby from class path and leave there only HSQL. Spring Boot docs:
If HSQLDB is on your classpath, and you have not manually configured
any database connection beans, then we will auto-configure an
in-memory database.
If that's not an option, you can specify
connection type for Hibernate (JPA):
An embedded database is detected by looking at the Connection type: hsqldb, h2 and derby are embedded, the rest are not.
HSQL explicitly as primary data source:
#Configuration
public class DataSourceConfig {
#Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.build();
}
}
define spring.datasource.type property

How to get a DataSource?

I'm not certain how to get a DataSource object. I was able to use the DriverManager method to obtain a connection to a SQL database running on localhost, but every time I try to use the DataSource method to do so I wind up getting exceptions (mostly for naming).
What I was wondering is:
Is it possible to get a DataSource object for local hosted databases?
Does the DataSource class need to be published, or is it like DriverManager where you just get a connection with no new class creation?
Could you show an example?
A DataSource allows getting a JDBC connection mostly from a pool of connections. A DataSource object represents a particular DBMS or some other data source, such as a file. If a company uses more than one data source, it will deploy a separate DataSource object for each of them. The DataSource interface is implemented by a driver vendor. You externalize DB connection properties file and fetch the object using JNDI. Using a Datasource you need to know only the JNDI name. The Application server cares about the details.
It can be implemented in three different ways:
A basic DataSource implementation produces standard Connection objects that are not pooled or used in a distributed transaction.
A DataSource implementation that supports connection pooling produces Connection objects that participate in connection pooling, that is, connections that can be recycled.
A DataSource implementation that supports distributed transactions produces Connection objects that can be used in a distributed transaction, that is, a transaction that accesses two or more DBMS servers.
Like, in Spring, you can configure the datasource in an XML file and then (1) either inject it into your bean, (2) get it from ApplicationContext.
DataSource ds = (DataSource) ApplicationContextProvider.
getApplicationContext().getBean("myDataSource");
Connection c = ds.getConnection();
Suggested Reading:
Connecting with DataSource Objects
Why do we use a DataSource instead of a DriverManager?
Data access with JDBC
How to retrieve DB connection using DataSource without JNDI?
Best way to manage DB connections without JNDI

Datasource for Liquibase as a Servlet Listener

I'm starting to configure Liquibase in my web.xml file, but I don't understand what the datasource attribute refers to:
<context-param>
<param-name>liquibase.datasource</param-name>
<param-value>java:comp/env/jdbc/default</param-value>
</context-param>
The documentation says it is a JNDI datasource, but I'm using Struts, not Spring, and have my connection details in different properties files (e.g. hibernate.dev.properties, hibernate.test.properties) which I load programatically depending on the current environment:
Configuration hibernateConfig = new Configuration();
hibernateConfig.addProperties("com/env.specific.properties");
To be honest, I don't have any idea of what is or how to use JNDI.
What should I write in that specific context-param value? Is there any way to do something similar to what I do with hibernate?
I am using Tomcat 6.0, in case that may help.
The property liquibase.datasource refers to the JNDI name of a DataSource object in your webapp's JNDI directory, as documented in the Liquibase manual.
Since you are not using JNDI, you cannot use the default Servlet listener LiquibaseServletListener provided by Liquibase. I assume you are creating JDBC resources directly, e.g. within your web application. You probably have a C3P0 DataSource connection pool somewhere or access to the underlying JDBC Connection in some way.
If this is the case, you could roll your own initializing and inject the JDBC Connection into Liquibase as follows:
DataSource dataSource = ... // get from Hibernate somehow
Connection connection = dataSource.getConnection();
JdbcConnection liquibaseConnection = new JdbcConnection(connection);
Liquibase liquibase = new Liquibase("mychangelog.xml",...,liquibaseConnection);
liquibase.update("");
This code snippet is not tested, but should be s.th. like that. You may add this in your own Servlet Context Initializer listener or in your app-specific code - any place where you have the Hibernate configuration at hand and can retrieve the DataSource. As a start, have a look into the sources of liquibase.servlet.LiquibaseServletListener how to do that.

Categories