Multiple SessionFactory configuration fails with org.springframework.beans.factory.UnsatisfiedDependencyException Exception - spring

Our application need to deal with multiple databases. We tried to configure multiple data sources through Hibernate configuration and added two configurations one for database 1 and second for database 2. This configuration fails with following Exception
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'DB1TransactionManager' defined in class path resource [org/npcc/ccms/config/db/HibernateConfig4DB1.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [org.hibernate.SessionFactory]: : No qualifying bean of type [org.hibernate.SessionFactory] is defined: expected single matching bean but found 2: AgrgtrSessionFactory,HRSessionFactory,StageSessionFactory; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.hibernate.SessionFactory] is defined: expected single matching bean but found 3: DB1SessionFactory,DB2SessionFactory
org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:747)
org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:462)
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1094)
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:989)
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:703)
org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:658)
org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:530)
org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:484)
org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136)
javax.servlet.GenericServlet.init(GenericServlet.java:160)
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:947)
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1009)
org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
java.lang.Thread.run(Thread.java:745)
root cause
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.hibernate.SessionFactory] is defined: expected single matching bean but found 2: DB1SessionFactory,DB2SessionFactory
org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:970)
org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:858)
org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:811)
org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:739)
org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:462)
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1094)
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:989)
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:504)
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:703)
org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
org.springframework.web.servlet.FrameworkServlet.configureAndRefreshWebApplicationContext(FrameworkServlet.java:658)
org.springframework.web.servlet.FrameworkServlet.initWebApplicationContext(FrameworkServlet.java:530)
org.springframework.web.servlet.FrameworkServlet.initServletBean(FrameworkServlet.java:484)
org.springframework.web.servlet.HttpServletBean.init(HttpServletBean.java:136)
javax.servlet.GenericServlet.init(GenericServlet.java:160)
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:947)
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1009)
org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
java.lang.Thread.run(Thread.java:745)
First Database Configuration:
#Configuration
#EnableTransactionManagement
#ComponentScan({ "org.npcc.ccms.config" })
#PropertySource(value = { "classpath:application.properties" })
public class HibernateConfig4DB1 {
final static Logger logger = LogManager.getLogger(HibernateConfig4DB1.class);
#Autowired
private Environment environment;
#Bean(name="DB1SessionFactory")
public LocalSessionFactoryBean db1SessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(new String[] { "org.npcc.ccms.model.db1" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean(destroyMethod="")
public DataSource dataSource() {
JndiTemplate jndi = new JndiTemplate();
DataSource dataSource = null;
try {
dataSource = (DataSource) jndi.lookup(environment.getRequiredProperty("datasource"));
} catch (NamingException e) {
logger.error("NamingException for java:comp/env/jdbc/ccms_cp1_orcl", e);
}
return dataSource;
}
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
properties.put("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql"));
return properties;
}
#Bean(name="DB1TransactionManager")
#Autowired
#Qualifier("DB1SessionFactory")
public HibernateTransactionManager db1TransactionManager(SessionFactory s) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
}
Second database configuration:
#Configuration
#EnableTransactionManagement
#ComponentScan({ "org.npcc.ccms.config" })
#PropertySource(value = { "classpath:application.properties" })
public class HibernateConfig4DB2 {
final static Logger logger = LogManager.getLogger(HibernateConfig4DB2.class);
#Autowired
private Environment environment;
#Bean(name="DB2SessionFactory")
public LocalSessionFactoryBean db2SessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(new String[] { "org.npcc.ccms.model.db2" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean(destroyMethod="")
public DataSource dataSource() {
JndiTemplate jndi = new JndiTemplate();
DataSource dataSource = null;
try {
dataSource = (DataSource) jndi.lookup(environment.getRequiredProperty("datasource"));
} catch (NamingException e) {
logger.error("NamingException for java:comp/env/jdbc/ccms_cp1_orcl", e);
}
return dataSource;
}
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
properties.put("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql"));
return properties;
}
#Bean(name="DB2TransactionManager")
#Autowired
#Qualifier("DB2SessionFactory")
public HibernateTransactionManager db2TransactionManager(SessionFactory s) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
}

That's because you defined 2 SessionFactory in your configuration and Spring can't guess which one to pick.
You could autowire your sessionFactory and use it in your #Bean :
#Autowired
#Qualifier("DB1SessionFactory")
private SessionFactory sessionFactory;
#Bean(name="DB1TransactionManager")
public HibernateTransactionManager db2TransactionManager() {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(this.sessionFactory);
return txManager;
}
To explicitly choose the bean to use.
Moreover you should note that only one configuration need to hold #EnableTransactionManagement, plus you are component scanning twice the same package which is unnecessary. Again, you have 2 #PropertySource with the same properties file, only one will be needed.
In your case I would create a RootHibernateConfig holding #Configuration, #EnableTransactionManagement, #ComponentScan({ "org.npcc.ccms.config" }) and #PropertySource(value = { "classpath:application.properties" }) annotations + #Import with your HibernateConfig4DB1 and HibernateConfig4DB2.
You can remove #Autowired and #Qualifier annotations from your transactionManager beans methods. Don't forget to explicitly use the needed transactionManager in your code.
See : http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/transaction/annotation/Transactional.html
Spring - Is it possible to use multiple transaction managers in the same application?

I faced the same problem . Solved like following way
#Bean(name = "rebootSessionFactory")
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(
new String[]
{
"com.ra.reboot.b.persistence.entity",
"com.ra.reboot.bl.persistence.entity",
"com.ra.reboot.bla.persistence.entity",
"com.ra.reboot.blaBla.persistence.entity"
});
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean(name="rebootTransactionManager")
public HibernateTransactionManager transactionManager() {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(sessionFactory().getObject());
return txManager;
}
private Properties hibernateProperties() {
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.cache.use_second_level_cache", "true");
hibernateProperties.setProperty("hibernate.cache.region.factory_class", "org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory");
hibernateProperties.setProperty("hibernate.cache.use_query_cache", "true");
hibernateProperties.setProperty("org.hibernate.envers.audit_table_prefix", "history");
hibernateProperties.setProperty("org.hibernate.envers.audit_table_suffix", "_audit");
hibernateProperties.setProperty("hibernate.jdbc.batch_size", "500");
return hibernateProperties;
}
#Bean
public HibernateExceptionTranslator hibernateExceptionTranslator() {
return new HibernateExceptionTranslator();
}
#Bean
public DataSource dataSource() {
Properties jdbcProperties = this.getJdbcPropertise();
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(jdbcProperties.getProperty("jdbc.drive.class"));
dataSource.setUrl(jdbcProperties.getProperty("jdbc.database.url"));
dataSource.setUsername(jdbcProperties.getProperty("jdbc.database.username"));
dataSource.setPassword(jdbcProperties.getProperty("jdbc.database.password"));
return dataSource;
}
private Properties getJdbcPropertise() {
InputStream inputStream = RarebootDataSourceConfig.class.getClassLoader().getResourceAsStream("dev.jdbc.properties");
Properties jdbcProperties = new Properties();
try {
jdbcProperties.load(inputStream);
} catch (NullPointerException IOException) {
System.out.println("dev.jdbc.properties not Found");
throw new RuntimeException();
} catch (IOException e) {
System.out.println("dev.jdbc.properties not Found");
throw new RuntimeException();
}
return jdbcProperties;
}
This is the config file. Then in the BaseDao
#Autowired
#Qualifier("rebootSessionFactory")
public SessionFactory sessionFactory;
public SessionFactory getSessionFactory() {
return sessionFactory;
}
public Session getSession() {
return this.sessionFactory.getCurrentSession();
}
In my Service
import org.springframework.transaction.annotation.Transactional;
#Service
#Transactional(value = "rebootTransactionManager")
public class ClientLeadInfoService {
...
}
Make sure you are using this :
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
So I have 2 config file(Here I post only one example file). I have to tell my BaseDao which session factory I am using for which. In my service I have to tell which transaction manager I am using.

It was happening due to inheritance. there was class BaseClass (base class) and class DerrivedClass (extended from BaseClass). When I was using #Autowire annotation, it was giving out an error with reason there are 2 objects i.e. Class BaseClass and Class DerrivedClass.
I used #Resource annotation instead of #Autowire as below
#Resource(name="derrivedClass")
DerrivedClass testObj;
It seems Spring boot creates beans with names same as class in camel letter. e.g. if my class is TestClass then the bean name would be "testClass"

Related

Could not initialize class org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider

I'm trying to learn Spring Data JPA and I'm getting the following exception:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in org.mql.gestionstages.config.PersistanceJPAConfig: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean]: Factory method 'entityManagerFactory' threw exception; nested exception is java.lang.NoClassDefFoundError: Could not initialize class org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider
I have the following configuration class for JPA :
#Configuration
#EnableTransactionManagement
public class PersistanceJPAConfig {
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan(new String[] { "mypackage.models" });
**JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();**
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
return em;
}
#Bean
public DriverManagerDataSource dataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/gestion_stages");
dataSource.setUsername("root");
dataSource.setPassword("");
return dataSource;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
Properties additionalProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.hbm2ddl.auto", "create-drop");
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL57Dialect");
return properties;
}
}
The exception is thrown in this line:
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
These are all the jars I have in my /WEB-INF/lib folder:
aspectjweaver-1.9.5.jar
hibernate-core-5.2.17.Final.jar
hibernate-entitymanager-5.2.17.Final.jar
hibernate-jpa-2.1-api-1.0.2.Final.jar
hibernate-jpamodelgen-5.2.17.Final.jar
javax.servlet.jsp.jstl-1.2.2.jar
javax.servlet.jsp.jstl-api-1.2.2.jar
mysql-connector-java-8.0.13.jar
org.eclipse.persistence.jpa-2.6.8.jar
spring-aop-5.2.2.RELEASE.jar
spring-aspects-5.2.2.RELEASE.jar
spring-beans-5.2.2.RELEASE.jar
spring-context-5.2.2.RELEASE.jar
spring-core-5.2.2.RELEASE.jar
spring-data-commons-2.2.2.RELEASE.jar
spring-data-jpa-2.2.2.RELEASE.jar
spring-expression-5.2.2.RELEASE.jar
spring-jcl-5.2.2.RELEASE.jar
spring-jdbc-5.2.2.RELEASE.jar
spring-orm-5.2.2.RELEASE.jar
spring-tx-5.2.2.RELEASE.jar
spring-web-5.2.2.RELEASE.jar
spring-webmvc-5.2.2.RELEASE.jar
The root exception is NoClassDefFoundError: Could not initialize class org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider which basically means you are missing a jar.
To gather all the required jars for an application is a PITA, I therefore strongly suggest to switch to a dependency management solution like Maven where you declare a dependency to Spring Data JPA and it gathers all the transient dependencies for you.

Could not autowire hibernate SessionFactory in dao class

i have a problem injecting a hibernate sessionfactory bean in my dao class. saying there is no bean qualified with that name
#Repository("emaildao")
public class EmailDAOImpl implements EmailDAO{
private SessionFactory sessionFactory;
#Autowired
public void setSessionFactory(SessionFactory s){
sessionFactory = s;
}
#Override
public void saveMessages(List<Message> messages) {
// some code persisting messages
}
here is my config files
#Configuration
#EnableTransactionManagement
#ComponentScan({ "com.opm.emailheader" })
#PropertySource(value = { "classpath:application.properties" })
public class HibernateConfiguration {
#Autowired
private Environment environment;
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(new String[] { "com.opm.emailheader"} );
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(environment.getRequiredProperty("jdbc.driverClassName"));
dataSource.setUrl(environment.getRequiredProperty("jdbc.url"));
dataSource.setUsername(environment.getRequiredProperty("jdbc.username"));
dataSource.setPassword(environment.getRequiredProperty("jdbc.password"));
return dataSource;
}
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
properties.put("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql"));
properties.put("hibernate.query_cache", environment.getRequiredProperty("hibernate.query_cache"));
properties.put("hibernate.query_cache_size", environment.getRequiredProperty("hibernate.query_cache_size"));
properties.put("hibernate.hbm2ddl.auto", environment.getRequiredProperty("hibarnate.hbm2ddl.auto"));
properties.put("hibernate.connection.CharSet","utf8");
properties.put("hibernate.connection.characterEncoding","utf8");
properties.put("hibernate.connection.useUnicode",true);
return properties;
}
#Bean
#Autowired
public HibernateTransactionManager transactionManager(SessionFactory s) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
the code structure folder is like:
com.opm.emailheader
app
dao
daoimpl
conf
HibernateConfiguration.java
AppInitializer.java
WebConfig.java
SecurityConfig.java
the exception stack trace :
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'emaildao': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void com.opm.emailheader.app.dao.daoimpl.EmailDAOImpl.setSessionFactory(org.hibernate.SessionFactory); nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.hibernate.SessionFactory] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
project structure

How to configure multiple datasources using java Hibernate configuration class

We are configuring Hibernate using java, here is our code to configure hibernate.
#Configuration
#EnableTransactionManagement
#ComponentScan({ "org.npcc.ccms.config" })
#PropertySource(value = { "classpath:application.properties" })
public class HibernateConfiguration {
final static Logger logger = LogManager.getLogger(HibernateConfiguration.class);
#Autowired
private Environment environment;
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(new String[] { "org.npcc.ccms.model" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean(destroyMethod="")
public DataSource dataSource() {
JndiTemplate jndi = new JndiTemplate();
DataSource dataSource = null;
try {
dataSource = (DataSource) jndi.lookup(environment.getRequiredProperty("datasource"));
} catch (NamingException e) {
logger.error("NamingException for java:comp/env/jdbc/ccms_cp1_orcl", e);
}
return dataSource;
}
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
properties.put("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql"));
return properties;
}
#Bean
#Autowired
public HibernateTransactionManager transactionManager(SessionFactory s) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
}
My question is how can configure another datasource within same hibernate configuration class? I found solution here but using xml configuration, how this works using java configuration? Thanks in advance.
You will need two different beans annotated as follows:
#Bean(name="SessionFactory")
public SessionFactory sessionFactory() {
}
and:
#Bean(name="OtherSessionFactory")
public SessionFactory otherSessionFactory() {
}
And two datasources configured appropriately.
Then when you want to use the other SessionFactory you just need:
#Autowired
#Qualifier("SessionFactory")
SessionFactory sessionFactory
or
#Autowired
#Qualifier("OtherSessionFactory")
SessionFactory sessionFactory

Multiple Transaction Managers in Spring Boot for different EntityManagers

I need to connect to two different databases from a single application. The trouble is that my appEntityManager does not have a transaction manager associated with it and I am not sure how to do it. The #Primary adminEntityManager is able to use the one provided by spring boot without any trouble as described here.
The configuration above almost works on its own. To complete the
picture you need to configure TransactionManagers for the two
EntityManagers as well. One of them could be picked up by the default
JpaTransactionManager in Spring Boot if you mark it as #Primary. The
other would have to be explicitly injected into a new instance. Or you
might be able to use a JTA transaction manager spanning both.
I have annoted the configuration with
#EnableTransactionManagement
And here is the relavant beans
#Bean
#ConfigurationProperties(prefix = "datasource.app")
public DataSource appDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
#Primary
#ConfigurationProperties(prefix = "datasource.admin")
public DataSource adminDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
public LocalContainerEntityManagerFactoryBean appEntityManagerFactory(
final EntityManagerFactoryBuilder builder) {
return builder
.dataSource(appDataSource())
.packages("au.com.mycompany.app.bomcommon.domain")
.persistenceUnit("appPersistentUnit")
.build();
}
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean adminEntityManagerFactory(
final EntityManagerFactoryBuilder builder) {
return builder
.dataSource(adminDataSource())
.packages("au.com.mycompany.app.bombatch")
.persistenceUnit("adminPersistentUnit")
.build();
}
//I thought this would do it but I am getting an exception
//No qualifying bean of type [org.springframework.transaction.PlatformTransactionManager] is defined: expected single matching bean but found 2: appTransactionManager,transactionManager
#Bean
public JpaTransactionManager appTransactionManager(#Qualifier("appEntityManagerFactory") final EntityManagerFactory emf) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
Update
I ended up doing it a different way. see here.
See if this works:
#Bean
#Primary
#ConfigurationProperties(prefix = "datasource.admin")
public DataSource adminDS() { ... }
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean adminEMF(...) { ... }
#Bean
#Primary
public JpaTransactionManager adminTM(...) { ... }
#Bean
public LocalContainerEntityManagerFactoryBean appEMF(...) { ... }
#Bean
public JpaTransactionManager appTM(...) { ... }
The only change I have made from your configuration is to declare a transaction manager for the admin side explicitly and marked that transaction manager as the default.
See the below changes. It works for me. Created 3 Data Sources, 3 Session Factories, and 3 Transaction Managers. Added these transaction managers in chainedTransaction as per below:
#Configuration
#EnableTransactionManagement
public class HibernateConfiguration implements TransactionManagementConfigurer {
#Bean("chainedTransactionManager")
public PlatformTransactionManager transactionManager(
#Qualifier("transactionManager1") final HibernateTransactionManager transactionManager1,
#Qualifier("transactionManager2") final HibernateTransactionManager transactionManager2,
#Qualifier("transactionManager3") final HibernateTransactionManager transactionManager3) {
return new ChainedTransactionManager(transactionManager1, transactionManager2, transactionManager3);
}
#Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
// TODO Auto-generated method stub
return transactionManager(oneTransactionManager(), twoTransactionManager(), threeTransactionManager());
}
#Bean(name = "oneDataSource", destroyMethod="")
public DataSource oneDataSource() throws IllegalArgumentException, NamingException, SQLException {
HikariConfig hkConfig = new HikariConfig();
System.out.println(env.getRequiredProperty("spring1-datasource.jdbcUrl"));
hkConfig.setJdbcUrl(env.getRequiredProperty("spring1-datasource.jdbcUrl"));
hkConfig.setUsername(env.getRequiredProperty("spring1-datasource.username"));
hkConfig.setPassword(env.getRequiredProperty("spring1-datasource.password"));
hkConfig.setDriverClassName(env.getRequiredProperty("spring1-datasource.driverClassName"));
return new HikariDataSource(hkConfig);
}
#Bean(name="oneSessionFactory")
#Qualifier("oneSessionFactory")
public LocalSessionFactoryBean oneSessionFactory() throws IllegalArgumentException, NamingException, SQLException {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(oneDataSource());
sessionFactory.setPackagesToScan(env.getRequiredProperty("entitymanager.packagesToScan"));
Properties hibernateProperties = new Properties();
hibernateProperties.put("hibernate.dialect", env.getRequiredProperty("hibernate.dialect"));
hibernateProperties.put("hibernate.show_sql", env.getRequiredProperty("hibernate.show_sql"));
sessionFactory.setHibernateProperties(hibernateProperties);
return sessionFactory;
}
#Bean(name="transactionManager1")
public HibernateTransactionManager oneTransactionManager() {
HibernateTransactionManager oneTransactionManager = new HibernateTransactionManager();
try {
oneTransactionManager.setSessionFactory(oneSessionFactory().getObject());
} catch (IllegalArgumentException | NamingException | SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return oneTransactionManager
}
#Bean(name = "twoDataSource", destroyMethod="")
public DataSource twoDataSource() throws IllegalArgumentException, NamingException, SQLException {
HikariConfig hkConfig = new HikariConfig();
System.out.println(env.getRequiredProperty("spring2-datasource.jdbcUrl"));
hkConfig.setJdbcUrl(env.getRequiredProperty("spring2-datasource.jdbcUrl"));
hkConfig.setUsername(env.getRequiredProperty("spring2-datasource.username"));
hkConfig.setPassword(env.getRequiredProperty("spring2-datasource.password"));
hkConfig.setDriverClassName(env.getRequiredProperty("spring2-datasource.driverClassName"));
return new HikariDataSource(hkConfig);
}
#Bean(name="twoSessionFactory")
#Qualifier("twoSessionFactory")
public LocalSessionFactoryBean twoSessionFactory() throws IllegalArgumentException, NamingException, SQLException {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(twoDataSource());
sessionFactory.setPackagesToScan(env.getRequiredProperty("entitymanager.packagesToScan"));
Properties hibernateProperties = new Properties();
hibernateProperties.put("hibernate.dialect", env.getRequiredProperty("hibernate.dialect"));
hibernateProperties.put("hibernate.show_sql", env.getRequiredProperty("hibernate.show_sql"));
sessionFactory.setHibernateProperties(hibernateProperties);
return sessionFactory;
}
#Bean(name="transactionManager2")
public HibernateTransactionManager twoTransactionManager() {
HibernateTransactionManager twoTransactionManager = new HibernateTransactionManager();
try {
twoTransactionManager.setSessionFactory(twoSessionFactory().getObject());
} catch (IllegalArgumentException | NamingException | SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return twoTransactionManager
}
#Bean(name = "threeDataSource", destroyMethod="")
public DataSource threeDataSource() throws IllegalArgumentException, NamingException, SQLException {
HikariConfig hkConfig = new HikariConfig();
System.out.println(env.getRequiredProperty("spring3-datasource.jdbcUrl"));
hkConfig.setJdbcUrl(env.getRequiredProperty("spring3-datasource.jdbcUrl"));
hkConfig.setUsername(env.getRequiredProperty("spring3-datasource.username"));
hkConfig.setPassword(env.getRequiredProperty("spring3-datasource.password"));
hkConfig.setDriverClassName(env.getRequiredProperty("spring3-datasource.driverClassName"));
return new HikariDataSource(hkConfig);
}
#Bean(name="threeSessionFactory")
#Qualifier("threeSessionFactory")
public LocalSessionFactoryBean threeSessionFactory() throws IllegalArgumentException, NamingException, SQLException {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(threeDataSource());
sessionFactory.setPackagesToScan(env.getRequiredProperty("entitymanager.packagesToScan"));
Properties hibernateProperties = new Properties();
hibernateProperties.put("hibernate.dialect", env.getRequiredProperty("hibernate.dialect"));
hibernateProperties.put("hibernate.show_sql", env.getRequiredProperty("hibernate.show_sql"));
sessionFactory.setHibernateProperties(hibernateProperties);
return sessionFactory;
}
#Bean(name="transactionManager3")
public HibernateTransactionManager threeTransactionManager() {
HibernateTransactionManager threeTransactionManager = new HibernateTransactionManager();
try {
threeTransactionManager.setSessionFactory(threeSessionFactory().getObject());
} catch (IllegalArgumentException | NamingException | SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return threeTransactionManager
}
}

BeanDefinitionStoreException exception since trying to switch app to javaconfig from xml config

I am running into some configuration issue since trying to switch to javaconfig from xml config.
Here is the problematic configuration class:
#Configuration
#EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
#Profile({ "default", "cloud" })
public class DataConfiguration {
#Value("${database.driverClassName}")
private String driverClassName;
#Value("${database.url}")
private String url;
#Value("${database.username}")
private String username;
#Value("${database.password}")
private String password;
#Value("${database.validationQuery}")
private String validationQuery;
#Bean
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setTestOnBorrow(Boolean.TRUE);
dataSource.setTestOnReturn(Boolean.TRUE);
dataSource.setTestWhileIdle(Boolean.TRUE);
dataSource.setTimeBetweenEvictionRunsMillis(1800000);
dataSource.setNumTestsPerEvictionRun(3);
dataSource.setMinEvictableIdleTimeMillis(1800000);
dataSource.setValidationQuery(validationQuery);
dataSource.setMaxActive(5);
dataSource.setLogAbandoned(Boolean.TRUE);
dataSource.setRemoveAbandoned(Boolean.TRUE);
dataSource.setRemoveAbandonedTimeout(10);
return dataSource;
}
#Bean
public JpaTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory());
return transactionManager;
}
#Bean
public HibernateJpaVendorAdapter jpaVendorAdapter() {
return new HibernateJpaVendorAdapter();
}
#Bean
public HibernatePersistence persistenceProvider() {
return new HibernatePersistence();
}
#Bean(name = "entityManagerFactory", destroyMethod = "close")
public EntityManagerFactory entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setPackagesToScan("com.bignibou.domain");
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPersistenceProvider(persistenceProvider());
entityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter());
entityManagerFactoryBean.setJpaPropertyMap(propertiesMap());
return entityManagerFactoryBean.getObject();
}
public Map<String, String> propertiesMap() {
Map<String, String> propertiesMap = new HashMap<>();
propertiesMap.put("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");
propertiesMap.put("hibernate.hbm2ddl.auto", "update");
propertiesMap.put("hibernate.ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
propertiesMap.put("hibernate.connection.charSet", "UTF-8");
propertiesMap.put("hibernate.show_sql", "true");
propertiesMap.put("hibernate.format_sql", "true");
propertiesMap.put("hibernate.use_sql_comments", "true");
return propertiesMap;
}
#Bean
public HibernateExceptionTranslator hibernateExceptionTranslator() {
return new HibernateExceptionTranslator();
}
}
Here is the exception I get:
Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public static javax.persistence.EntityManager org.springframework.orm.jpa.SharedEntityManagerCreator.createSharedEntityManager(javax.persistence.EntityManagerFactory)] threw exception; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:181)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:570)
... 121 more
Caused by: java.lang.NullPointerException
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.initProxyClassLoader(SharedEntityManagerCreator.java:151)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.<init>(SharedEntityManagerCreator.java:143)
at org.springframework.orm.jpa.SharedEntityManagerCreator.createSharedEntityManager(SharedEntityManagerCreator.java:118)
at org.springframework.orm.jpa.SharedEntityManagerCreator.createSharedEntityManager(SharedEntityManagerCreator.java:96)
at org.springframework.orm.jpa.SharedEntityManagerCreator.createSharedEntityManager(SharedEntityManagerCreator.java:64)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:160)
... 122 more
It seems there's an issue with the entityManagerFactory config... What I am getting wrong?
If you are not using the entityManagerFactory() method anywhere in your java configuration, you can instead return the LocalContainerEntityManagerFactoryBean object.
The LocalContainerEntityManagerFactoryBean is both an InitializingBean and a FactoryBean. These are special interfaces that Spring can use to initialize a bean and then add it to the context.
You could therefore change your method to
#Bean(name = "entityManagerFactory", destroyMethod = "close")
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setPackagesToScan("com.bignibou.domain");
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPersistenceProvider(persistenceProvider());
entityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter());
entityManagerFactoryBean.setJpaPropertyMap(propertiesMap());
return entityManagerFactoryBean;
}
Spring will take care of calling afterPropertiesSet() and getObject() on the object returned by the method and adding the created EntityManagerFactory bean to the context.
This is detailed in the IoC chapter of the Spring documentation.
adding the following line sorted the issue:
entityManagerFactoryBean.setJpaPropertyMap(propertiesMap());
entityManagerFactoryBean.afterPropertiesSet();//NOTICE HERE!!!
return entityManagerFactoryBean.getObject();

Resources