Spring Boot Hibernate auto configuration vs manual annotated config - spring

I have a Spring Boot Web app that tries to access my database using Hibernate.
I also have a different, none spring boot application that tries to access the same database.
I am trying to configure the spring boot application using the auto configuration approach, defining my DB properties in the application.properties file.
I am configuring the none spring boot app using an annotated class.
For some reason, the Spring Boots auto configuration is acting differently to the annotated class configuration.
If I connect to the database for the first time and create the schema using hibernate ddl, and then I reconnect using the other way of configuration, I get ddl errors, and for example enumerated columns stop working.
Can someone explain why I get different behaviour using the two ways of configuring spring and hibernate and how do I get them to act in the same way? Could this have something to do with the ejb naming strategy property?
Here are the two ways I'm configuring the JPA:
application.properties:
spring.datasource.url=jdbc:hsqldb:file:databaseFiles/hibData/;hsqldb.write_delay_millis=0
spring.datasource.root=sa
spring.datasource.password=1
spring.datasource.driverClassName=org.hsqldb.jdbcDriver
spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.HSQLDialect
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.properties.hibernate.jdbc.batch_size=50
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
spring.jpa.properties.hibernate.jdbc.batch_versioned_data=true
Config class:
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.Properties;
#Configuration
#EnableTransactionManagement
public class DatabaseHibConfig {
#Bean
public DataSource getDataSource(){
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("org.hsqldb.jdbcDriver");
ds.setUrl("jdbc:hsqldb:file:databaseFiles/hibData/;hsqldb.write_delay_millis=0");
ds.setUsername("sa");
ds.setPassword("1");
return ds;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory, DataSource dataSource){
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setDataSource(dataSource);
jpaTransactionManager.setEntityManagerFactory(entityManagerFactory);
return jpaTransactionManager;
}
#Bean
LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource);
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
entityManagerFactoryBean.setPackagesToScan("mediabeast.data.hibernate.model");
Properties jpaProperties = new Properties();
jpaProperties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
jpaProperties.put("hibernate.hbm2ddl.auto","update");
jpaProperties.put("hibernate.show_sql","true");
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
}

adding the following properties to both configs seemed to fix the errors that stopped the code from working.
spring.jpa.properties.hibernate.physical_naming_strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.properties.hibernate.implicit_naming_strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl
however I am still getting the "object already exists" ddl errors on startup when switching configs.

Related

How can I configure multiple databases with springboot one being JPA and the other 5 plain jdbc?

For a project with a springboot backend, I have to use 1 database in Postgresql to save information and to add a history feature to the application and 5 other databases in Oracle on which I only have readonly access on views. For those 5 I only have to get ponctual values and the databases are huge so it doesn't make sense to have repositories and entities. But I can't find any information on how to configure multiple databases if one uses ORM and the others don't.
application.properties
#informations for the history database
spring.datasource.url = jdbc:postgresql://localhost:5432/NA_db
spring.datasource.username = userNA
spring.datasource.password = userNApwd
spring.datasource.driver-class-name=org.postgresql.Driver
spring.session.jdbc.initialize-schema: always
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
DataSourceConfiguration.java
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.zaxxer.hikari.HikariDataSource;
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "historyEntityManagerFactory", transactionManagerRef = "historyTransactionManager", basePackages = "mypackage.history.dao")
#EntityScan("mypackage.history.model.entities")
public class DataSourceConfig {
#Primary
#Bean
#ConfigurationProperties("spring.datasource")
public DataSourceProperties SourceProperties() {
return new DataSourceProperties();
}
#Primary
#Bean(name = "historyDatasource")
#ConfigurationProperties(prefix = "spring.datasource.configuration")
public DataSource dataSource() {
return SourceProperties().initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
#Primary
#Bean(name = "historyEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean barEntityManagerFactory(EntityManagerFactoryBuilder builder,
#Qualifier("historyDatasource") DataSource dataSource) {
return builder.dataSource(dataSource).packages("mypackage.history.model.entities")
.persistenceUnit("historyPU").build();
}
#Primary
#Bean(name = "historyTransactionManager")
public PlatformTransactionManager barTransactionManager(
#Qualifier("historyEntityManagerFactory") EntityManagerFactory barEntityManagerFactory) {
return new JpaTransactionManager(barEntityManagerFactory);
}
}
Connection and Statement creation for the other databases
Class.forName("oracle.jdbc.OracleDriver");
connectionYG = DriverManager.getConnection(urlYG, user, password);
stmtYG = connectionYG.createStatement();
connectionZA = DriverManager.getConnection(urlZA, user, password);
stmtZA = connectionZA.createStatement();
connectionZB = DriverManager.getConnection(urlZB, user, password);
stmtZB = connectionZB.createStatement();
connectionZC = DriverManager.getConnection(urlZC, user, password);
stmtZC = connectionZC.createStatement();
connectionZD = DriverManager.getConnection(urlZD, user, password);
stmtZD = connectionZD.createStatement();
Here is what I tried with a configuration class for the history database and instantiation of the connections directly where I need them in my service class.
Here is what I get :
***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
Action:
Consider the following:
If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).
I am pretty sure I just don't configurate my databases the correct way but I can't find the correct informations. Have you any ideas on how am I supposed to do ?

Ehcache - Cannot find cache name for Builder

I've looked through a lot of similar questions asked here but I'm still not able to find a solution so here's my issue:
I'm trying to setup Ehcache on springboot.
Spring 2.2.6.RELEASE
Ehcache 3.8.1
CacheService
I've got a cache named `myCache`.
#Cacheable(value = "myCache")
#GetMapping("/get")
public String get();
CacheConfig
And my config
#Configuration
#EnableCaching
public class CacheConfig {
public CacheConfig() {
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().withCache("myCache",
CacheConfigurationBuilder.newCacheConfigurationBuilder(SimpleKey.class, String.class, ResourcePoolsBuilder.heap(10))).build();
cacheManager.init();
}
}
Error
But I'm getting the following error:
java.lang.IllegalArgumentException: Cannot find cache named 'myCache' for Builder...
I managed to get it to work if I put the config in the xml file, but I rather have it in java.
#Cacheable(value = "myCache") doesn't create a cache named myCache in Ehcache. At runtime, if a cache named myCache is available in Ehcache, it'll use that cache for caching. If not, when attempting to cache at runtime, the exception java.lang.IllegalArgumentException: Cannot find cache named 'myCache' will be thrown. For #Cacheable(value = "myCache") to work with Ehcache as the backend, the cache needs to be created somewhere and Spring needs to be made aware of that cache. The simplest way to do that is to include the spring-boot-starter-cache dependency, add an ehcache.xml with the Ehcache config to classpath and set the config spring.cache.jcache.config: classpath:ehcache.xml in application.yml. You can find a sample application that does that on github
Instead if you do want to configure Ehcache programmatically, you need a org.springframework.cache.CacheManager bean, to initialize the Ehcache config and link it to Spring. The bean definition could look something like below:
import javax.cache.Caching;
import org.ehcache.config.CacheConfiguration;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.jsr107.Eh107Configuration;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.SimpleKey;
import org.springframework.cache.jcache.JCacheCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
#EnableCaching
public class CacheConfig {
#Bean
public CacheManager ehCacheManager() {
CacheConfiguration<SimpleKey, String> cacheConfig = CacheConfigurationBuilder
.newCacheConfigurationBuilder(SimpleKey.class, String.class, ResourcePoolsBuilder.heap(10))
.build();
javax.cache.CacheManager cacheManager = Caching.getCachingProvider("org.ehcache.jsr107.EhcacheCachingProvider")
.getCacheManager();
String cacheName = "myCache";
cacheManager.destroyCache(cacheName);
cacheManager.createCache(cacheName, Eh107Configuration.fromEhcacheCacheConfiguration(cacheConfig));
return new JCacheCacheManager(cacheManager);
}
}
Sample working application that configures Ehcache for Spring through code can be found here on github.

How to enable connection pooling in spring boot embedded tomcat

I have a spring boot application which is not a web application. In this application i have configured embedded tomcat with the help of following bean.
#Bean
public TomcatServletWebServerFactory tomcatFactory() {
return new TomcatServletWebServerFactory() {
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
tomcat.enableNaming();
return super.getTomcatWebServer(tomcat);
}
protected void postProcessContext(Context context) {
ContextResource contextResource = new ContextResource();
contextResource.setName("jdbc/BPMDB");
contextResource.setType(DataSource.class.getName());
contextResource.setProperty("driverClassName", env.getProperty("bpm.db.driverClassName"));
contextResource.setProperty("url", env.getProperty("bpm.db.url"));
contextResource.setProperty("username", env.getProperty("bpm.db.username"));
contextResource.setProperty("password", env.getProperty("bpm.db.password"));
context.getNamingResources().addResource(contextResource);
}
};
}
How do i do connection pooling for this embedded tomcat. I am using spring boot 2.x which says hikaricp is the default connection pooling but how to set it into this embedded tomcat.
Does this require to set properties like spring.datasource.hikari.initial-size=15
spring.datasource.hikari.max-wait=20000
but again how boot will know and how will i know that these properties are used.
Thanks.
I have got answer for my problem.
Its simple. We just have to make a DataSource reference and autowire it and mention database related properties along with hikari related properties.
Code is below.
#Autowired
public DataSource dataSource;
Add above to your #Configuration marked class and add following properties to application.properties file.
spring.datasource.driver-class=...
spring.datasource.url=jdbc:oracle:thin:....
spring.datasource.username=..
spring.datasource.password=..
spring.datasource.hikari.initial-size=15
spring.datasource.hikari.max-wait=20000
spring.datasource.hikari.max-active=50
spring.datasource.hikari.max-idle=50
spring.datasource.hikari.min-idle=8
Also i have written a test case to check for hikari connection pool. Below is the code.
import javax.sql.DataSource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#SpringBootTest(
properties = "spring.datasource.type=com.zaxxer.hikari.HikariDataSource",
classes = {ApplicationConfiguration.class,PersistenceJpaContext.class}
)
public class HikariConnectionPoolTest {
#Autowired
private DataSource dataSource;
#Test
public void hikariConnectionPoolIsConfigured() {
assertEquals("com.zaxxer.hikari.HikariDataSource", dataSource.getClass().getName());
}
}

How to connect to PostgreSQL in Spring Boot 2 using a Bean

I would like to connect to PostgreSQL using a bean (#Bean) and Spring Data.
At this bean, I would like to use my own customer properties in application.properties file, like these properties below
my.db.user=postgres
my.db.password=root
my.db.url=jdbc:postgresql://localhost:5432/mydb
I can't use use default properties (spring.datasource.url , spring.datasource.username, etc .. )
Spring Boot 2 by default uses Hikari to manager connections.
So, you can do it.
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
#Configuration
public class DatabaseConfig {
#Bean
#Primary
#ConfigurationProperties("spring.datasource")
public HikariDataSource dataSource() {
return DataSourceBuilder.create()
.type(HikariDataSource.class)
.build();
}
}
application.properties
spring.datasource.platform=postgres
spring.datasource.jdbc-url=jdbc:postgresql://localhost:5432/dbname
spring.datasource.username=postgres
spring.datasource.password=123456
If you wanna use tomcat-jdbc, add the following config.
spring.datasource.type=org.apache.tomcat.jdbc.pool.DataSource
Reference link : https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0.0-M2-Release-Notes#default-connection-pool
You can create your own DataSource with your custom database properties the following way.
#Bean
#ConfigurationProperties(prefix="my.db")
public DataSource myDataSource() {
return DataSourceBuilder.create().build();
}
Have a look at the following description to configure two databases for a full example, this explains how you can use your own properties
two databases example

spring-boot 1.5.2+.RELEASE: Use more than one datasource with the same connection string

We have a spring boot application using hikariCP as connection pooler and multiple datasources to handle long jobs processing (basically it uses the same connection string with longer timeouts and a reduced number of connections)
Since 1.5.2.RELEASE upgrade, only the first datasource annotated with #Primary has its housekeeping and pool threads starting. The secondary one is simply discarded, although the debug shows the datasource initialization being executed.
With 1.5.1.RELEASE this worked properly, both group of threads would start.
Here are our datasources definitions
import java.util.Properties;
import javax.sql.DataSource;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import lombok.AllArgsConstructor;
#Profile("hikari")
#Validated
#AllArgsConstructor(onConstructor = #__(#Autowired))
#Component
class HikariDataSourceConfig
{
private final DataSourceConfig dataSourceConfig;
protected Properties setDataSourceProperties()
{
final Properties dataSourceProperties = new Properties();
dataSourceProperties.put("driverType", "thin");
dataSourceProperties.put("user", this.dataSourceConfig.getUsername());
dataSourceProperties.put("password", this.dataSourceConfig.getPassword());
dataSourceProperties.put("serverName", this.dataSourceConfig.getServer());
dataSourceProperties.put("portNumber", String.valueOf(this.dataSourceConfig.getPort()));
dataSourceProperties.put("databaseName", this.dataSourceConfig.getSid());
return dataSourceProperties;
}
protected HikariDataSource initializeDataSource(final int maximumPoolSize)
{
final HikariDataSource dataSource = new HikariDataSource();
dataSource.setMaximumPoolSize(maximumPoolSize);
dataSource.setValidationTimeout(this.dataSourceConfig.getValidationTimeout() * 1000L);
dataSource.setDataSourceClassName("oracle.jdbc.pool.OracleDataSource");
dataSource.setConnectionTimeout(this.dataSourceConfig.getConnectionTimeout() * 1000L);
dataSource.setMaxLifetime(this.dataSourceConfig.getMaxLifetime() * 1000L);
dataSource.setIdleTimeout(this.dataSourceConfig.getIdleTimeout() * 1000L);
dataSource.setAutoCommit(false);
return dataSource;
}
#Bean(destroyMethod = "close")
#Primary
public DataSource dataSource()
{
final HikariDataSource dataSource = initializeDataSource(this.dataSourceConfig.getMaximumPoolSize());
dataSource.setPoolName(DataSourceConfig.CONNECTION_POOL_DEFAULT_NAME);
dataSource.setLeakDetectionThreshold(this.dataSourceConfig.getLeakDetectionThreshold() * 1000L);
dataSource.setDataSourceProperties(setDataSourceProperties());
return dataSource;
}
#Bean(name = DataSourceConfig.DATASOURCE_ALT_NAME, destroyMethod = "close")
public DataSource slowDataSource()
{
final HikariDataSource dataSource = initializeDataSource(this.dataSourceConfig.getWorkingQueueSize());
dataSource.setMinimumIdle(1);
dataSource.setPoolName(DataSourceConfig.CONNECTION_POOL_ALT_NAME);
dataSource.setLeakDetectionThreshold(this.dataSourceConfig.getSlowJobLeakDetectionThreshold() * 1000L);
dataSource.setDataSourceProperties(setDataSourceProperties());
return dataSource;
}
}
Is there a way to configure spring to allow cloned datasources again?
Versions used:
HikariCP: 2.6.1
Spring-boot: 1.5.3.RELEASE
Java: 1.8.121
-- Edit
It fails for both 1.5.2.RELEASE and 1.5.3.RELEASE
-- Edit 2
I tried to bind the second datasource to a different database, but the same issue occurs, the secondary datasource pool does not start.

Resources