Issue with Spring Data JPA - BeanEntityManagerFactory - spring

I recently started learning spring. I am trying a simple example with spring data jpa in a spring mvc project. I am getting the following error while deploying the war file in the tomcat.
Caused by: org.springframework.beans.factory.BeanCreationException: Error creati
ng bean with name '(inner bean)#584d15f2': Cannot resolve reference to bean 'ent
ityManagerFactory' while setting constructor argument; nested exception is org.s
pringframework.beans.factory.BeanCurrentlyInCreationException: Error creating be
an with name 'entityManagerFactory': Requested bean is currently in creation: Is
there an unresolvable circular reference?
at org.springframework.beans.factory.support.BeanDefinitionValueResolver
.resolveReference(BeanDefinitionValueResolver.java:359)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver
.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)
at org.springframework.beans.factory.support.ConstructorResolver.resolve
ConstructorArguments(ConstructorResolver.java:634)
at org.springframework.beans.factory.support.ConstructorResolver.instant
iateUsingFactoryMethod(ConstructorResolver.java:444)
at org.springframework.beans.factory.support.AbstractAutowireCapableBean
Factory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:11
19)
at org.springframework.beans.factory.support.AbstractAutowireCapableBean
Factory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1014)
at org.springframework.beans.factory.support.AbstractAutowireCapableBean
Factory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
at org.springframework.beans.factory.support.AbstractAutowireCapableBean
Factory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver
.resolveInnerBean(BeanDefinitionValueResolver.java:299)
... 92 more
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: E
rror creating bean with name 'entityManagerFactory': Requested bean is currently
in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistr
y.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:347)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistr
y.getSingleton(DefaultSingletonBeanRegistry.java:223)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBe
an(AbstractBeanFactory.java:299)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean
(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver
.resolveReference(BeanDefinitionValueResolver.java:351)
... 100 more
27-Sep-2015 17:56:45.304 INFO [http-apr-8080-exec-35] org.apache.catalina.startu
p.HostConfig.deployWAR Deployment of web application archive D:\ApacheTomcat\apa
che-tomcat-8.0.26\webapps\springTest.war has finished in 6,124 ms
My controller code is as follows,
package com.demo.repo;
import com.demo.model.Customer;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Properties;
import javax.activation.DataSource;
import javax.persistence.EntityManagerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
/**
* Handles requests for the application home page.
*/
#Controller
#Configuration
#EnableJpaRepositories("com.demo.repo")
#EnableTransactionManagement
public class HomeController {
#Autowired
customerRepository repository;
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
/**
* Simply selects the home view to render by returning its name.
*/
#Bean(destroyMethod = "close")
DataSource dataSource(Environment env) {
HikariConfig dataSourceConfig = new HikariConfig();
dataSourceConfig.setDriverClassName(env.getRequiredProperty("db.driver"));
dataSourceConfig.setJdbcUrl(env.getRequiredProperty("db.url"));
dataSourceConfig.setUsername(env.getRequiredProperty("db.username"));
dataSourceConfig.setPassword(env.getRequiredProperty("db.password"));
return (DataSource) new HikariDataSource(dataSourceConfig);
}
#Bean
LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource,
Environment env) {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource((javax.sql.DataSource) dataSource);
entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
entityManagerFactoryBean.setPackagesToScan("com.demo.repo");
Properties jpaProperties = new Properties();
//Configures the used database dialect. This allows Hibernate to create SQL
//that is optimized for the used database.
jpaProperties.put("hibernate.dialect", env.getRequiredProperty("hibernate.dialect"));
//Specifies the action that is invoked to the database when the Hibernate
//SessionFactory is created or closed.
jpaProperties.put("hibernate.hbm2ddl.auto",
env.getRequiredProperty("hibernate.hbm2ddl.auto")
);
//Configures the naming strategy that is used when Hibernate creates
//new database objects and schema elements
jpaProperties.put("hibernate.ejb.naming_strategy",
env.getRequiredProperty("hibernate.ejb.naming_strategy")
);
//If the value of this property is true, Hibernate writes all SQL
//statements to the console.
jpaProperties.put("hibernate.show_sql",
env.getRequiredProperty("hibernate.show_sql")
);
//If the value of this property is true, Hibernate will format the SQL
//that is written to the console.
jpaProperties.put("hibernate.format_sql",
env.getRequiredProperty("hibernate.format_sql")
);
entityManagerFactoryBean.setJpaProperties(jpaProperties);
return entityManagerFactoryBean;
}
#Bean
JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
#RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
logger.info("Welcome home! The client locale is {}.", locale);
Date date = new Date();
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);
String formattedDate = dateFormat.format(date);
model.addAttribute("serverTime", formattedDate );
repository.save(new Customer("Jack", "Bauer"));
repository.save(new Customer("Chloe", "O'Brian"));
repository.save(new Customer("Kim", "Bauer"));
repository.save(new Customer("David", "Palmer"));
repository.save(new Customer("Michelle", "Dessler"));
for(Customer customer : repository.findAll())
{
System.out.println("Log Results :: "+customer.toString());
}
return "myhome";
}
}
Can anyone suggest me what is wrong with my code and any suggestions to resolve the same.

It seems that your entityManagerFactory requires the dataSource that is defined in the same configuration file.
Try moving the definition of dataSource to another configuration class, or, instead of passing the dataSource as parameter, just call the dataSource() method when you need it in the entityManagerFactory.
#Autowired
Environment env;
#Bean
LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean =
new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource((javax.sql.DataSource) dataSource());
....
}
TIP: Don't mix your #Controller and #Configuration. Create a different file for each of them.

Related

Hibernate single table inheritance creating columns for subclasses in springboot [duplicate]

Note that this code does work with plain Spring but not with Spring Boot(v1.3.3), is there something i'm missing because this is imported from a spring app that works. The code below is from the spring boot app
#Entity
#Table(name="project")
public class Project implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id")
private int id;
#Column(name="teamId")
private int teamId;
//private String Rentabiliteit;
#Column
//#Index(name="IProject_status",columnNames="Status")
private String status;
#Column
//#Index(name="IProject_naam",columnNames="Naam")
private String naam;
//public Prototype m_Prototype;
//public Team m_Team;
}
SQL
CREATE TABLE IF NOT EXISTS `project` (
`id` int(11) NOT NULL,
`teamId` int(11) DEFAULT NULL,
`status` varchar(255) DEFAULT NULL,
`naam` varchar(255) DEFAULT NULL
) ENGINE=InnoDB AUTO_INCREMENT=43 DEFAULT CHARSET=latin1;
ERROR
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException:
Unknown column 'project0_.team_id' in 'field list'
Edited: Application.yml
spring:
mvc:
view:
prefix: /WEB-INF/jsp/
suffix: .jsp
datasource:
url: jdbc:mysql://localhost:3306/oxyplast
username: oxyplastuser
password: oxyplastuserpw
jpa:
properties:
hibernate:
current_session_context_class: org.springframework.orm.hibernate4.SpringSessionContext
namingStrategy: org.hibernate.cfg.DefaultNamingStrategy
SINCE SPRING-BOOT 1.4
Starting from 1.4, because of the switch to Hibernate 5, the naming strategy has been updated to SpringPhysicalNamingStrategy which should be very close to 1.3 defaults.
See also:
Spring's naming strategy
PREVIOUS VERSION
Spring Boot provides the ImprovedNamingStrategy as default naming strategy, which makes Hibernate search for a team_id column (inferred from the int teamId field). As this column doesn't exist in your table, that's the cause of the error. From the Hibernate docs:
An improved naming strategy that prefers embedded underscores to mixed case names
You've got two options:
Provide the column name explicitly as #Column(name="teamId"). There used to be a bug with this in early Boot versions, not anymore.
Change the naming strategy in the Spring Boot properties and tell it to use the EJB3NamingStrategy, which doesn't convert camelCase to snake_case, but keeps it as it is.
If you are using Spring Boot 2.0.2 and Hibernate 5.3.4 then setting the following property will fix the issue.
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
Below strategy worked for me
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.DefaultComponentSafeNamingStrategy
with the late version :
spring-boot-starter-data-jpa: ➡ 1.5.2.RELEASE
hibernate-core:5.0.12.Final
this class
PhysicalNamingStrategyStandardImpl
needs to be extended and added to hibernate properties.
Here is a full working version
public class PhysicalNamingStrategyImpl extends PhysicalNamingStrategyStandardImpl implements Serializable {
public static final PhysicalNamingStrategyImpl INSTANCE = new PhysicalNamingStrategyImpl();
#Override
public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
String nameModified;
// Do whatever you want with the name modification
return new Identifier(nameModified, name.isQuoted());
}
}
#Override
public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
String nameModified;
// Do whatever you want with the name modification
return new Identifier(nameModified, name.isQuoted());
}
Linking it to hibernate should be done like this when configuring the datasource.
properties.put("hibernate.physical_naming_strategy", "my.Package.PhysicalNamingStrategyImpl");
here is a full working version of datasource config
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
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 javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactory",
basePackages = { "com.xxxxxx.repository" }
)
public class SharedDataSourceConfig {
#Value("${startup.ddl-auto}")
String hbm2ddl;
#Primary
#Bean(name = "dataSource")
#ConfigurationProperties("spring.datasource.shared")
public DataSource customerDataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder builder,
#Qualifier("dataSource") DataSource dataSource) {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("hibernate.hbm2ddl.auto", hbm2ddl);
properties.put("hibernate.physical_naming_strategy", "my.package.PhysicalNamingStrategyImpl");
return builder
.dataSource(dataSource)
.packages(PackageScannerHelper.getPackagesToScan())
.persistenceUnit("shared")
.properties(properties)
.build();
}
#Primary
#Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(
#Qualifier("entityManagerFactory") EntityManagerFactory
entityManagerFactory
) {
return new JpaTransactionManager(entityManagerFactory);
}
}
This worked for me with spring boot 1.4.0 and hibernate entitymanager 4.3.8.Final
application.properties
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
application.properties
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.DefaultComponentSafeNamingStrategy
the above properties work for me.
hibernate 4.3.11.Final
spring boot 1.4.2.RELEASE

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 ?

Spring beans in configuration file form a cycle (Kotlin, Redis)

I'm new to Kotlin and working on a Spring application, trying to set Redis configuration. I keep getting this problem:
org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'redisConfig': Requested bean is
currently in creation: Is there an unresolvable circular reference?
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| redisConfig defined in file [file]
└─────┘
I don't understand what exactly causes this problem and how to fix it. It seems to me that RedisConfig is being created inside RedisConfig, but I'm not sure and don't understand where this problem comes from.
Here's RedisConfig.kt
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.data.redis.connection.MessageListener
import org.springframework.data.redis.connection.RedisStandaloneConfiguration
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.data.redis.listener.ChannelTopic
import org.springframework.data.redis.listener.RedisMessageListenerContainer
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer
#Configuration
class RedisConfig(val messageListener: MessageListener) {
#Value("\${spring.redis.host}")
lateinit var redisHost: String
#Value("\${spring.redis.port}")
lateinit var redisPort: String
#Value("\${spring.redis.topic}")
lateinit var redisTopic: String
#Bean
fun jedisConnectionFactory(): JedisConnectionFactory {
val config = RedisStandaloneConfiguration(redisHost, redisPort.toInt())
val jedisClientConfiguration = JedisClientConfiguration.builder().usePooling().build()
val factory = JedisConnectionFactory(config, jedisClientConfiguration)
factory.afterPropertiesSet()
return factory
}
#Bean
fun redisTemplate(): RedisTemplate<String, Any> {
val template: RedisTemplate<String, Any> = RedisTemplate()
template.connectionFactory = JedisConnectionFactory()
template.valueSerializer = GenericJackson2JsonRedisSerializer()
return template
}
#Bean
fun topic(): ChannelTopic = ChannelTopic(redisTopic)
#Bean
fun newMessageListener(): MessageListenerAdapter = MessageListenerAdapter(messageListener)
#Bean
fun redisContainer(): RedisMessageListenerContainer {
val container = RedisMessageListenerContainer()
container.connectionFactory = jedisConnectionFactory()
container.addMessageListener(newMessageListener(), topic())
return container
}
}
I believe you should use your method jedisConnectionFactory() instead of a new JedisConnectionFactory class, in redisTemplate method.
Basicly, you should do the same as you do in redisContainer()

Retrive JMS ConnectionFactory from JNDI using Spring Boot auto-configuration

I want to use the spring boot autoconfigure for JMS to connect to a remote JNDI and retrieve the ConnectionFactory based on his name populated through the spring.jms.jndi-name property in the application.properties file.
I noticed that the spring boot autoconfigure is relying on the JndiConnectionFactoryAutoConfiguration class to do that and this class in turn will call the JndiTemplate class to do the lookup. The problem is that the value of the environment attribute of the JndiTemplate class is null, so we cannot create the intialContext.
In fact, I noticed that the JndiTemplate class is instantiated with no-argument constructor in starting application and before loading the configuration defined in the JndiConnectionFactoryAutoConfiguration class.
My question: how can I instantiate JndiTemplate by specifying a list of properties (Context.INITIAL_CONTEXT_FACTORY, Context.PROVIDER_URL..)? knowing that JmsTemplate has a constructor that takes an Properties object.
Just for information: my application is a simple jar that doesn’t run on a server at the moment.
For those interested in the answer, you must use VM options to pass required JNDI properties.
Here is an example that works with ActiveMQ:
VM options:
-Djava.naming.provider.url=tcp://hostname:61616
-Djava.naming.factory.initial=org.apache.activemq.jndi.ActiveMQInitialContextFactory
And spring properties file (application.properties) must contain the JNDI name of the connection factory:
spring.jms.jndi-name=ConnectionFactory
Much better, you can use configuration to fin your connection factory from JNDI. In my project, we finished by creating our jms starter that we can use in all microservices.
Properties class:
import lombok.*;
import org.springframework.boot.context.properties.ConfigurationProperties;
#Getter
#Setter
#ToString
#NoArgsConstructor
#EqualsAndHashCode
#ConfigurationProperties( prefix = "custom.jms" )
public class CustomJmsProperties {
private String jndiName;
private String contextFactoryClass;
private String providerUrl;
private String username;
private String password;
}
Configuration class:
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter;
import org.springframework.jndi.JndiLocatorDelegate;
import javax.jms.ConnectionFactory;
import javax.naming.Context;
import javax.naming.NamingException;
import java.util.Properties;
#Configuration
#ConditionalOnProperty( "custom.jms.jndi-name" )
#ConditionalOnMissingBean( ConnectionFactory.class )
#EnableConfigurationProperties( { CustomJmsProperties.class } )
#AutoConfigureAfter( { JndiConnectionFactoryAutoConfiguration.class } )
public class CustomJndiConnectionFactoryAutoConfiguration {
#Bean
public ConnectionFactory connectionFactory( CustomJmsProperties customJmsProperties ) throws NamingException {
ConnectionFactory connectionFactory = lookupForConnectionFactory( customJmsProperties );
return getEnhancedUserCredentialsConnectionFactory( customJmsProperties, connectionFactory );
}
private ConnectionFactory lookupForConnectionFactory( final CustomJmsProperties customJmsProperties ) throws NamingException {
JndiLocatorDelegate jndiLocatorDelegate = new JndiLocatorDelegate();
Properties jndiProperties = getJndiProperties( customJmsProperties );
jndiLocatorDelegate.setJndiEnvironment( jndiProperties );
return jndiLocatorDelegate.lookup( customJmsProperties.getJndiName(), ConnectionFactory.class );
}
private Properties getJndiProperties( final CustomJmsProperties customJmsProperties ) {
Properties jndiProperties = new Properties();
jndiProperties.setProperty( Context.PROVIDER_URL, customJmsProperties.getProviderUrl() );
jndiProperties.setProperty( Context.INITIAL_CONTEXT_FACTORY, customJmsProperties.getContextFactoryClass() );
if ( StringUtils.isNotEmpty( customJmsProperties.getUsername() ) ) {
jndiProperties.setProperty( Context.SECURITY_PRINCIPAL, customJmsProperties.getUsername() );
}
if ( StringUtils.isNotEmpty( customJmsProperties.getPassword() ) ) {
jndiProperties.setProperty( Context.SECURITY_CREDENTIALS, customJmsProperties.getPassword() );
}
return jndiProperties;
}
private UserCredentialsConnectionFactoryAdapter getEnhancedUserCredentialsConnectionFactory( final CustomJmsProperties customJmsProperties,
final ConnectionFactory connectionFactory ) {
UserCredentialsConnectionFactoryAdapter enhancedConnectionFactory = new UserCredentialsConnectionFactoryAdapter();
enhancedConnectionFactory.setTargetConnectionFactory( connectionFactory );
enhancedConnectionFactory.setUsername( customJmsProperties.getUsername() );
enhancedConnectionFactory.setPassword( customJmsProperties.getPassword() );
enhancedConnectionFactory.afterPropertiesSet();
return enhancedConnectionFactory;
}
}
Properties file of your project:
custom.jms.provider-url=tcp://hostname:61616
custom.jms.context-factory-class=org.apache.activemq.jndi.ActiveMQInitialContextFactory
custom.jms.jndi-name=ConnectionFactory

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