EntityManagerFactory.close() not closing HikariDataSource set by hibernate.connection.datasource-property - java

I'm using Hibernate 5.4.18 with HikariCP 3.4.5. My configuration is programmatic, and I set underlying DataSource of Hibernate with hibernate.connection.datasource-property. Strangely, when I then call EntityManagerFactory.close()-function, it doesn't call close()-method of HikariDataSource, and connection will leave open. Is this a desired behavior? Oracle documentation says that EntityManagerFactory.close() will "Close the factory, releasing any resources that it holds".
Minimum example with Kotlin:
fun main() {
val emf = Persistence.createEntityManagerFactory("default", getJpaProperties())
// Fetch underlying HikariDataSource
val ds = emf.unwrap(SessionFactoryImpl::class.java)
.serviceRegistry
.getService<ConnectionProvider>(ConnectionProvider::class.java)
.unwrap(HikariDataSource::class.java)
emf.close()
println(ds.isClosed) // prints "false"
}
private fun getJpaProperties(): Map<String, Any> {
val dataSource = HikariDataSource().apply {
username = "sa"
password = ""
jdbcUrl = "jdbc:h2:mem:test_db"
}
return mapOf(
"hibernate.dialect" to "org.hibernate.dialect.H2Dialect",
"hibernate.connection.datasource" to dataSource
)
}

It's because you are providing an instance of a datasource. If you initialise a DS, there's a big chance you'll use it in other parts of your code, so closing the datasource would introduce an unexpected behaviour. This is actually a good practice, that the "module" that creates a resource is also responsible for disposing of it.
Hibernate will close the datasource if you provide the details of it (username, password, class name, etc), as it will be managed by Hibernate.
For a bit of history, in the old days, a DS would be created in by a J2EE container (e.g. Tomcat) and then shared across many apps inside that container. And the property hibernate.connection.datasource would be a JNDI location pointing to the datasource.

Related

Simple-JNDI VS SimpleNamingContextBuilder with EmbeddedDatabaseBuilder and already existing Data Source

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);

HikariCP Address Specification and Documentation

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.

spring - read environment variables from inside the application.properties file

I specified Spring properties inside the application.properties file. How can i populate those properties from the environment variables?
Here is what I tried, but it doesn't seem to work:
application.properties
spring.datasource.url=jdbc:postgresql://#{ systemProperties['DATABASE_HOST']}:5432/dbname
spring.datasource.username = postgres
spring.datasource.password = postgres
You can refer to environment properties in the same way you refer to Spring properties using ${...} syntax.
In your case:
spring.datasource.url=jdbc:postgresql://${DATABASE_HOST}:5432/dbname
Out of the box, as you know, spring-boot expects its Datasource details to be using a specific set of variable names. Being spring of course you can rework this if you need by a few methods:
1/ If the need to use variables from the environment comes from deployment to a cloud service such as Cloud Foundry or Horuku, there is spring-boot-starter-cloud-connector which handles allot of the plumbing out of the box. A good read is the (Binding to Data Services with Spring Boot in Cloud Foundry article and the Deploying to the cloud docs which walks you thru this
2/ Instead of relying on Spring-Boot's own auto-magical wiring mechanism, you can create a custom configuration bean to override how the DataSource information is populated. A good read explaining the annotations involved can be found here: Spring Java Config Documentation - #Bean Configuration JavaDOC. Based on your example above, here is what I spat out:
#Configuration
public class MyDataSourceConfig {
#Bean
#Primary
public DataSource getDataSource() {
String url = "jdbc:postgresql://" + System.getenv("DATABASE_HOST") + ":5432/dbname";
String username = "postgres";
String password = "postgres";
String driverClassName = "org.postgresql.Driver";
/*
* Create the datasource and return it
*
* You could create the specific DS
* implementation (ie: org.postgresql.ds.PGPoolingDataSource)
* or ask Spring's DataSourceBuilder to autoconfigure it for you,
* whichever works best in your eyes
*/
return DataSourceBuilder
.create()
.url( url )
.username( username )
.password( password )
.driverClassName( driverClassName )
.build();
}
}
Just remember that in spring, you can always override allot of the default behaviours with a little bit of digging!
Hope this helps!
You don't have to. When Spring Boot initializes its environment, it pulls stuff from both the application.properties file and any system-level variables and combines them together. The full list of locations where Spring takes them from is here, specifically points 9) and 10).

How to access non-static properties in the creation of a Spring Factory Bean?

Looking for any type of solution here, but I'll propose my thought process along the way.
Working with Spring-Mybatis (over a MySql database) and running it through the Maven embedded jetty plugin.
So it's a pretty simple issue I'm running into and easy to re-create. Basically, Mybatis-Spring requires one to define a Spring bean for the SqlSessionFactoryBean class. This class implements Spring's FactoryBean interface. To wire it up properly, one should supply it, among other things, a DataSource. I reference the JDBC connection details elsewhere in code (for Mybatis-Generator purposes) and would like to keep those details consolidated to one place: property file or something else.
For now, I have a property file, and everything built pretty standard from a Spring point-of-view. I start seeing issues when trying to boot up the Jetty server though, Mybatis-Spring seems to catch itself in a circular dependency.
I cannot say I quite know exactly what causes this circular reference, but I do know the point of failure: I set the JDBC driver to null when trying to call (from an autowired Environment env.getRequiredProperty("jdbc.driver"). It's an NPE (on the env object); the property later gets initialized as I print it out in a PostConstructor.
My understanding of this issue is that since the SqlSessionFactoryBean is a Spring factory bean (does this make it of BFPP type?), that normal autowiring and bean instantiation cannot be assumed to be available. What further solidifies this point is that if I hard-code the JDBC details (or otherwise make the SqlSessionFactoryBean method static), then Jetty is able to start-up just fine and the application works as expected. Obviously hard-coding in the JDBC details is not desired here -- I reference these details in one other spot.
tl;dr
How can I statically access Spring's environment property values, say, in a Factory Bean method? I have tried an #autowired+postconstruct route and a implement-application-context-aware approach; both to no avail.
code samples
public abstract class AbstractMybatisConfig {
#Autowired
Environment env;
String jdbcDriver;
String jdbcUrl;
String jdbcUsername;
String jdbcPassword;
// if I try the below statement, the env object throws an NPE
// jdbcDriver = env.getRequiredProperty( "jdbc.driver" );
#PostConstruct
protected void postConstruct() {
jdbcDriver = env.getRequiredProperty( "jdbc.driver" );
// this statement prints out "com.mysql.jdbc.Driver" as expected
System.out.println( jdbcDriver );
jdbcUrl = env.getRequiredProperty( "jdbc.url" );
jdbcUsername = env.getRequiredProperty( "jdbc.username" );
jdbcPassword = env.getRequiredProperty( "jdbc.password" );
}
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName( jdbcDriver );
// this statement prints out null
System.out.println( jdbcDriver );
dataSource.setUrl( jdbcUrl );
dataSource.setUsername( jdbcUsername );
dataSource.setPassword( jdbcPassword );
return dataSource;
}
#Bean // circular reference happens here b/c dataSource() sets null properties
public SqlSessionFactoryBean sqlSessionFactory() {
SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
sqlSessionFactory.setDataSource( dataSource() );
// ...other (unrelated) configuration goes here...
return sqlSessionFactory;
}
private static getJdbcProperties() {
Properties jdbcProps = new Properties();
// when this method is invoked, Spring complains the resource does not exist
// (and it is the same String used to identify this property file in my PropertySources annotation found in another config)
jdbcProps.load( new ClassPathResource( "classpath:META-INF/properties/mybatis.properties" ).getInputStream() );
// ...code to grab property values omitted for brevity...
return jdbcProps
}

How to get current Connection object in Spring JDBC

How can I get the current Connection object for an Oracle database? I'm using the JDBC module in Spring 3.0.5.
Obtain the Connection from the DataSource bean.
You can access the dataSource by using Spring dependency injection to inject it into your bean, or by accessing ApplicationContext statically:
DataSource ds = (DataSource)ApplicationContextProvider.getApplicationContext().getBean("dataSource");
Connection c = ds.getConnection();
Just an Info :
I am using Spring JDBC Template, which holds the current connection object for me, which can be received as follows.
Connection con;
con = getJdbcTemplate().getDataSource().getConnection();
Use DataSourceUtils.getConnection().
It returns connection associated with the current transaction, if any.
I'm not sure if this method was available when this question was originally posted, however, it seems the preferred way to do it in the latest version of Spring is with JdbcTemplate and PreparedStatementCreator. See https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/jdbc/core/JdbcTemplate.html#query-org.springframework.jdbc.core.PreparedStatementCreator-org.springframework.jdbc.core.PreparedStatementSetter-org.springframework.jdbc.core.ResultSetExtractor- or any of the other query methods that take a PreparedStatementCreator as the first param:
jdbcTemplate.query(con -> {
// add required logic here
return con.prepareStatement("sql");
}, rs -> {
//process row
});
This has the advantage over the other provided answers (DataSourceUtils.getConnection() or jdbcTemplate.getDataSource().getConnection() as a new connection is not allocated, it uses the same connection management it would as calling any of the other jdbcTemplate querying methods. You also therefore do not need to worry about closing / releasing the connection, since spring will handle it.

Categories

Resources