I am getting the error below when I try configure multiple datasources in Spring boot:
Property: xyz.integration.multidatasources.connections.datasource1.username
Value: safuser
Origin: class path resource [application.properties]:14:67
Reason: org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType
When I try to load multiple datasource configurations. The properties are as below:
xyz.integration.multidatasources.connections.datasource1.url=jdbc:postgresql://localhost:5432/safdb
xyz.integration.multidatasources.connections.datasource1.driver-class-name=org.postgresql.Driver
xyz.integration.multidatasources.connections.datasource1.username=auser
xyz.integration.multidatasources.connections.datasource1.password=apassword
The java class is as below:
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
#Configuration
#EnableConfigurationProperties
#ConfigurationProperties(prefix = "xyz.integration.multidatasources")
public class MultipleDatasourcesProperties {
private Map<String, DataSourceProperties> connections = new HashMap<>();
public Map<String, DataSourceProperties> getConnections() {
return connections;
}
public void setConnections(Map<String, DataSourceProperties> connections) {
this.connections = connections;
}
}
Am I missing anything in my configuration?
Can you try this ?
application.properties
primary.url={primary-database-url}
primary.username={primary-database-username}
primary.password={primary-database-password}
primary.driver-class-name=com.mysql.jdbc.Driver
primary.test-on-borrow=true
primary.validation-query=SELECT 1
secondary.url={secondary-database-url}
secondary.username={secondary-database-username}
secondary.password={secondary-database-password}
secondary.driver-class-name=com.mysql.jdbc.Driver
secondary.test-on-borrow=true
secondary.validation-query=SELECT 1
secondary.validation-interval=25200000
Create the configuration beans
#Bean(name = "primaryDataSource")
#ConfigurationProperties("primary")
#Primary
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "secondaryDataSource")
#ConfigurationProperties("secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
Next, create the JdbcTemplate beans that we are going to use for accessing the data sources in our data access layer.
#Bean(name = "primaryJdbcTemplate")
public JdbcTemplate primaryJdbcTemplate(#Qualifier("primary") DataSource primaryDs) {
return new JdbcTemplate(writeDs);
}
#Bean(name = “secondaryJdbcTemplate")
public JdbcTemplate secondaryJdbcTemplate(#Qualifier("secondary") DataSource secondaryDs) {
return new JdbcTemplate(secondaryDs);
}
Try accessing properties like this:
#Resource(name = "primaryJdbcTemplate")
private JdbcTemplate primaryJdbcTemplate;
#Resource(name = "secondaryJdbcTemplate")
private JdbcTemplate secondaryJdbcTemplate;
primaryJdbcTemplate.query(“any-query-to-apply-on-primary-data-source”);
Related
I want to implement a read-only routing for db, I have researched lots of ways to achieve this and came across the following repository.
I couldn't understand the function of the following two methods so I simplified and changed it in my repository in the following way.
transactionManager() & entityManagerFactory(EntityManagerFactoryBuilder builder)
If I keep having these methods I am getting the error , how could I overcome this error?
Thanks.
org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL "create index contact_tag_ix_uuid on contact_tag (uuid)" via JDBC Statement
at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.accept(GenerationTargetToDatabase.java:67) ~[hibernate-core-5.6.14.Final.jar:5.6.14.Final]
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.applySqlString(AbstractSchemaMigrator.java:581) ~[hibernate-core-5.6.14.Final.jar:5.6.14.Final]
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.applySqlStrings(AbstractSchemaMigrator.java:526) ~[hibernate-core-5.6.14.Final.jar:5.6.14.Final]
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.applyIndexes(AbstractSchemaMigrator.java:348) ~[hibernate-core-5.6.14.Final.jar:5.6.14.Final]
at org.hibernate.tool.schema.internal.GroupedSchemaMigratorImpl.performTablesMigration(GroupedSchemaMigratorImpl.java:88) ~[hibernate-core-5.6.14.Final.jar:5.6.14.Final]
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.performMigration(AbstractSchemaMigrator.java:220) ~[hibernate-core-5.6.14.Final.jar:5.6.14.Final]
at org.hibernate.tool.schema.internal.AbstractSchemaMigrator.doMigration(AbstractSchemaMigrator.java:123) ~[hibernate-core-5.6.14.Final.jar:5.6.14.Final]
at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.performDatabaseAction(SchemaManagementToolCoordinator.java:196) ~[hibernate-core-5.6.14.Final.jar:5.6.14.Final]
at org.hibernate.tool.schema.spi.SchemaManagementToolCoordinator.process(SchemaManagementToolCoordinator.java:85) ~[hibernate-core-5.6.14.Final.jar:5.6.14.Final]
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:335) ~[hibernate-core-5.6.14.Final.jar:5.6.14.Final]
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:471) ~[hibernate-core-5.6.14.Final.jar:5.6.14.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1498) ~[hibernate-core-5.6.14.Final.jar:5.6.14.Final]
package ru.project.master.slave.datasources;
import java.util.Map;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
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 com.google.common.collect.Maps;
import ru.project.master.slave.properties.Properties;
import ru.project.master.slave.routing.RoutingDataSource;
#Configuration
#ComponentScan
#EnableJpaRepositories("ru.project.master.slave.dao")
#EnableTransactionManagement
public class DataSourcesConfiguration {
#Autowired
private Properties props;
#Bean(name = "masterDataSource")
#Primary
public DataSource masterDataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(props.getProperty("spring.datasource.master.driver-class-name"));
dataSource.setDefaultAutoCommit(false);
dataSource.setUsername(props.getProperty("spring.datasource.master.username"));
dataSource.setPassword(props.getProperty("spring.datasource.master.password"));
dataSource.setUrl(props.getProperty("spring.datasource.master.url"));
return dataSource;
}
#Bean
public RoutingDataSource routingDataSource() {
RoutingDataSource dataSource = new RoutingDataSource();
dataSource.setDefaultTargetDataSource(masterDataSource());
Map<Object, Object> targets = Maps.newHashMap();
targets.put(DataSourceType.MASTER, masterDataSource());
targets.put(DataSourceType.SLAVE, slaveDataSource());
dataSource.setTargetDataSources(targets);
dataSource.afterPropertiesSet();
return dataSource;
}
#Bean(name = "slaveDataSource")
public DataSource slaveDataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(props.getProperty("spring.datasource.slave.driver-class-name"));
dataSource.setDefaultAutoCommit(false);
dataSource.setUsername(props.getProperty("spring.datasource.slave.username"));
dataSource.setPassword(props.getProperty("spring.datasource.slave.password"));
dataSource.setUrl(props.getProperty("spring.datasource.slave.url"));
return dataSource;
}
#Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(masterDataSource());
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder) {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(Boolean.parseBoolean(props.getProperty("spring.jpa.generate-ddl")));
Map<String, String> additionalProperties = Maps.newHashMap();
additionalProperties.put("hibernate.dialect", props.getProperty("spring.jpa.dialect"));
LocalContainerEntityManagerFactoryBean factory = builder.dataSource(routingDataSource())
.packages("ru.project.master.slave.orm").persistenceUnit("routingEMF").properties(additionalProperties)
.build();
factory.setJpaVendorAdapter(vendorAdapter);
factory.afterPropertiesSet();
return factory;
}
}
My solution:
But somehow If feels missing because I think I need to implement as have been mentioned here
https://stackoverflow.com/a/75223437/19751568
I need some configuration to provide some hibernate functionality.
Not only that the hibernate.connection.provider_disables_autocommit allows you to make better use of database connections, but it’s the only way we can make this example work since, without this configuration, the connection is acquired prior to calling the determineCurrentLookupKey method TransactionRoutingDataSource.
#Configuration
#ComponentScan
#("io.api.persistence.repository")
#EnableTransactionManagement
public class DataSourcesConfiguration {
#Value("${spring.datasource.master.url}")
private String mstUrl;
#Value("${spring.datasource.master.username}")
private String mstUsername;
#Value("${spring.datasource.master.password}")
private String mstPassword;
#Value("${spring.datasource.slave.url}")
private String slaveUrl;
#Value("${spring.datasource.slave.username}")
private String slaveUsername;
#Value("${spring.datasource.slave.password}")
private String slavePassword;
#Bean
public DataSource dataSource() {
RoutingDataSource masterSlaveRoutingDataSource = new RoutingDataSource();
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceType.MASTER, masterDataSource());
targetDataSources.put(DataSourceType.SLAVE, slaveDataSource());
masterSlaveRoutingDataSource.setTargetDataSources(targetDataSources);
// Set as all transaction point to master
masterSlaveRoutingDataSource.setDefaultTargetDataSource(masterDataSource());
return masterSlaveRoutingDataSource;
}
public DataSource slaveDataSource() {
HikariDataSource hikariDataSource = new HikariDataSource();
hikariDataSource.setJdbcUrl(slaveUrl);
hikariDataSource.setUsername(slaveUsername);
hikariDataSource.setPassword(slavePassword);
return hikariDataSource;
}
#Primary
public DataSource masterDataSource() {
HikariDataSource hikariDataSource = new HikariDataSource();
hikariDataSource.setJdbcUrl(mstUrl);
hikariDataSource.setUsername(mstUsername);
hikariDataSource.setPassword(mstPassword);
return hikariDataSource;
}
I am trying to implement rabbitmq in my Spring application, not spring boot.
So I added this configuration
import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class RabbitConfiguration {
#Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory =
new CachingConnectionFactory("localhost");
return connectionFactory;
}
#Bean
public AmqpAdmin amqpAdmin() {
return new RabbitAdmin(connectionFactory());
}
#Bean
public RabbitTemplate rabbitTemplate() {
return new RabbitTemplate(connectionFactory());
}
#Bean
public Queue myQueue() {
return new Queue("MyQueue");
}
}
Then from my service class I have used:-
public void sendViaTemplate(String msg){
ApplicationContext context =
new AnnotationConfigApplicationContext(RabbitConfiguration.class);
RabbitTemplate template = context.getBean(RabbitTemplate.class);
template.convertAndSend(QUEUE_NAME,"Hello from template "+msg);
}
#RabbitListener(queues = "MyQueue")
public void ListenToMyQueue( String in){
System.out.println("New Msg arrived"+in);
}
The convertAndSend seems working as expected, but When the message is pushed into the queue, the ListenToMyQueue should be auto executed as new element is inserted into the queue, right? Why this is not working?
For future refference, after many days of try, finally this listener configuration is working as expected. enter link description here
I get this exception with this Configuration class
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.orm.hibernate5.HibernateTemplate;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
#Configuration
#EnableTransactionManagement//enable tx management
#EnableWebMvc//enables webmvc
//load properties into spring container
#PropertySource("classpath:app.properties")
//all layered classes common package name
#ComponentScan("in.nit")
public class AppConfig {
#Autowired
private Environment env;
//1) DataSource
#Bean
public DataSource ds() {
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName(env.getProperty("db.driver"));
ds.setUrl(env.getProperty("db.url"));
ds.setUsername(env.getProperty("db.user"));
ds.setPassword(env.getProperty("db.pwd"));
return ds;
}
//2)SessionFactory
#Bean
public SessionFactory sf() {
LocalSessionFactoryBean sf = new LocalSessionFactoryBean();
sf.setDataSource(ds());
sf.setHibernateProperties(props());
sf.setPackagesToScan("in.nit.model");
return sf.getObject();
}
//3)HibernateProperties
public Properties props() {
Properties p = new Properties();
p.put("hibernate.dialect", env.getProperty("orm.dialect"));
p.put("hibernate.show_sql", env.getProperty("orm.showsql"));
p.put("hibernate.format_sql",env.getProperty("orm.fmtsql"));
p.put("hibernate.hbm2ddl.auto", env.getProperty("orm.hbm2ddl.auto"));
return p;
}
//4)HibernateTemplate
#Bean
public HibernateTemplate ht() {
HibernateTemplate ht = new HibernateTemplate();
ht.setSessionFactory(sf());
return ht;
}
//5)HibernateTx Manager
#Bean
public HibernateTransactionManager htx() {
HibernateTransactionManager htx = new HibernateTransactionManager();
htx.setSessionFactory(sf());
return htx;
}
//6)ViewResolver
#Bean
public InternalResourceViewResolver ivr() {
InternalResourceViewResolver ivr = new InternalResourceViewResolver();
ivr.setPrefix(env.getProperty("mvc.prefix"));
ivr.setSuffix(env.getProperty("mvc.suffix"));
return ivr;
}
}
and I get the exception
Error creating bean with name 'ht' defined in in.nit.config.AppConfig: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Property 'sessionFactory' is required
obviously it has something to do with session factory
But when i replace the config with the code below it works fine
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.orm.hibernate5.HibernateTemplate;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
#Configuration
#EnableTransactionManagement //enables HtX
#EnableWebMvc // Spring MVC Activated
//load properties into Spring container
#PropertySource("classpath:app.properties")
//all layered classes common package name
#ComponentScan("in.nit")
public class AppConfig2 {
#Autowired
private Environment env;
//1. DataSource
#Bean
public DataSource ds() {
BasicDataSource d=new BasicDataSource();
d.setDriverClassName(env.getProperty("db.driver"));
d.setUrl(env.getProperty("db.url"));
d.setUsername(env.getProperty("db.user"));
d.setPassword(env.getProperty("db.pwd"));
return d;
}
//2. SessionFactrory
#Bean
public LocalSessionFactoryBean sf() {
LocalSessionFactoryBean s=new LocalSessionFactoryBean();
s.setDataSource(ds());
s.setHibernateProperties(props());
//s.setAnnotatedClasses(ShipmentType.class);
s.setPackagesToScan("in.nit.model");
return s;
}
#Bean
public Properties props() {
Properties p=new Properties();
p.put("hibernate.dialect", env.getProperty("orm.dialect"));
p.put("hibernate.show_sql", env.getProperty("orm.showsql"));
p.put("hibernate.format_sql", env.getProperty("orm.fmtsql"));
p.put("hibernate.hbm2ddl.auto", env.getProperty("orm.hbm2ddl.auto"));
return p;
}
//3. HT
#Bean
public HibernateTemplate ht() {
HibernateTemplate h=new HibernateTemplate();
h.setSessionFactory(sf().getObject());
return h;
}
//4. HtxM
#Bean
public HibernateTransactionManager htx() {
HibernateTransactionManager htm=new HibernateTransactionManager();
htm.setSessionFactory(sf().getObject());
return htm;
}
//5. ViewResovler
#Bean
public InternalResourceViewResolver ivr() {
InternalResourceViewResolver v=new InternalResourceViewResolver();
v.setPrefix(env.getProperty("mvc.prefix"));
v.setSuffix(env.getProperty("mvc.suffix"));
return v;
}
}
Can anyone explain why is the exception is being thrown in the first case ?
And both classes AppConfig and AppCOnfig2 seem to be doing the same thing.
Can anyone point what out specifically where and which lines make a difference?
Definition : LocalSessionFactoryBean is the FactoryBean that creates a Hibernate SessionFactory. It provides the SessionFactory object and itself is not a SessionFactory. In case you are getting exception, you are not passing the sessionfactory object and instead passing factoryBean which causes illegalArgumentException.
Original documentation: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/orm/hibernate5/LocalSessionFactoryBean.html
I am trying to set up a second data source in my Spring application.
Below are the 2 configuration classes for the 2 data sources:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = "com.XYXYale.persistence.XY",
entityManagerFactoryRef = "awEntityManagerFactory",
transactionManagerRef= "awTransactionManager"
)
public class Datasource1DataSourceConfig {
#Value("${spring.first-datasource.url}")
private String url;
#Value("${spring.first-datasource.username}")
private String username;
#Value("${spring.first-datasource.password}")
private String pw;
#Value("${spring.first-datasource.driver-class-name}")
private String driver;
#Bean
#Primary
#ConfigurationProperties("spring.first-datasource")
public DataSourceProperties awDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#Primary
#Qualifier("awEntityManagerFactory")
public DataSource awDataSource() {
DriverManagerDataSource dataSource
= new DriverManagerDataSource();
dataSource.setDriverClassName(
driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(pw);
return dataSource;
}
#Primary
#Bean(name = "awEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean awEntityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(awDataSource());
em.setPersistenceUnitName("XY");
");
em.setPackagesToScan(new String[] { "com.XY.XY.domain.XY" });
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setDatabasePlatform("org.hibernate.dialect.MySQLDialect");
em.setJpaVendorAdapter(vendorAdapter);
return em;
}
#Primary
#Bean
public PlatformTransactionManager awTransactionManager() {
JpaTransactionManager transactionManager
= new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
awEntityManagerFactory().getObject());
return transactionManager;
}
}
The second Config class:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = "com.XYXYale.persistence.YX",
entityManagerFactoryRef = "sndEntityManagerFactory",
transactionManagerRef= "sndTransactionManager"
)
public class SndDataSourceConfig {
#Value("${spring.second-datasource.jdbcUrl}")
private String url;
#Value("${spring.second-datasource.username}")
private String username;
#Value("${spring.second-datasource.password}")
private String pw;
#Value("${spring.second-datasource.driver-class-name}")
private String driver;
#Bean
#ConfigurationProperties("spring.second-datasource")
public DataSourceProperties sndDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#Qualifier("sndEntityManagerFactory")
public DataSource sndDataSource() {
DriverManagerDataSource dataSource
= new DriverManagerDataSource();
dataSource.setDriverClassName(
driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(pw);
return dataSource;
}
#Bean(name = "sndEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean sndEntityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(sndDataSource());
em.setPersistenceUnitName("snd");
em.setPackagesToScan(new String[] { "com.XY.XY.domain.YX" });
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setDatabasePlatform("org.hibernate.dialect.MySQLDialect");
em.setJpaVendorAdapter(vendorAdapter);
return em;
}
#Bean
public PlatformTransactionManager sndTransactionManager() {
JpaTransactionManager transactionManager
= new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
sndEntityManagerFactory().getObject());
return transactionManager;
}
}
I have in com.XYXYale.persistence.XY a Spring data JPA Repo defined as follows
DemoRepo
#Repository
public interface DemoRepo extends CrudRepository<Demo, String>, DemoRepoCustom{
}
DemoRepoCustom
#NoRepositoryBean
public interface DemoRepoCustom {
Demo returnDemoContent();
}
DemoRepoImpl
public class DemoRepoImpl extends QuerydslRepositorySupport implements DemoRepoCustom {
public DemoRepoImpl() {
super(Demo.class);
}
public Demo returnDemoContent(){
return something;
}
}
The Repo is used in the following way
#Autowired
DemoRepo demoRepo;
I am getting this exception:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'demoRepositoryImpl': Unsatisfied dependency expressed through method 'setEntityManager' parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.persistence.EntityManager' available: expected single matching bean but found 2: org.springframework.orm.jpa.SharedEntityManagerCreator#0,org.springframework.orm.jpa.SharedEntityManagerCreator#1
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:678)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:376)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1411)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:307)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:277)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1255)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1175)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:595)
... 55 more
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.persistence.EntityManager' available: expected single matching bean but found 2: org.springframework.orm.jpa.SharedEntityManagerCreator#0,org.springframework.orm.jpa.SharedEntityManagerCreator#1
at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:221)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1233)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1175)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:670)
... 70 more
Anyone having a suggestion on how to solve that? I could think of injecting the correct entitymanager to each repo, however I have no idea how to do that.
Thanks in advance. Cant find any solution on here or other sites.
if you will need to connect to more than one data source.
and assuming that you have already your spring app, then use this code 👇:
in your application.properties add this code:
# DEFAULT CONFIG
spring.main.banner-mode=off
server.port=2020
# CUSTOM PREFIX FOR SQL SERVER
sqlserver.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
sqlserver.datasource.jdbcUrl=jdbc:sqlserver://192.168.9.44;databaseName=myAwesomeDB
sqlserver.datasource.username=someUser
sqlserver.datasource.password=somePass
# DEFAULT PREFIX FOR POSTGRES
spring.datasource.platform=postgres
spring.datasource.url=jdbc:postgresql://192.168.6.125:5432/wilmer
spring.datasource.username=admWinter
spring.datasource.password=wilmer43nlp
now create a class called DataSourceDBConfig.java and add this code:
package com.example.demo.config;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
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;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import javax.sql.DataSource;
/**
* #author Wilmer Villca
* #version 0.0.1
*/
#Configuration
public class DataSourceDBConfig {
private final Environment env;
#Autowired
public DataSourceDBConfig(Environment env) {
this.env = env;
}
#Bean
#Primary
#ConfigurationProperties(prefix = "sqlserver.datasource")
public DataSource dataSourceFromSqlServer() {
return DataSourceBuilder.create().build();
}
#Bean
#ConfigurationProperties(prefix = "spring.datasource")
public HikariDataSource dataSourceFromPostgres() {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setDriverClassName("org.postgresql.Driver");
hikariConfig.setJdbcUrl(env.getProperty("spring.datasource.url"));
hikariConfig.setUsername(env.getProperty("spring.datasource.username"));
hikariConfig.setPassword(env.getProperty("spring.datasource.password"));
return new HikariDataSource(hikariConfig);
}
#Bean
#Qualifier("jdbcTemplateSqlServer")
public JdbcTemplate jdbcTemplateSqlServer(#Qualifier("dataSourceFromSqlServer") DataSource ds) {
return new JdbcTemplate(ds);
}
#Bean
#Qualifier("jdbcTemplatePostgres")
public JdbcTemplate jdbcTemplatePostgres(#Qualifier("dataSourceFromPostgres") DataSource ds) {
return new JdbcTemplate(ds);
}
}
that is all you need to to access multiple databases 😉
NOTE: Do not forget and
remember that this only is an alternative, exist anothers many ways 🎉
i hope you understand, 👀
These are my classes and configurations,
ServiceImpl.java
#Override
#Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public InsertUpdateSuccessBean insertNewSiteEntry(SiteEntry newSiteBean) throws SQLException {
dao.insert1();
dao.insert2();
except();
return new InsertUpdateSuccessBean(true,LoggerConstants.NEW_SITE_ENTRY_SUCCESS);
}
private void except()throws SQLException{
throw new SQLException();
}
DAOImpl.java
#Repository
public class DAOImpl implements DAO
{
#Autowired
private DataSourceTransactionManager transManager;
#Autowired
private CommonDAOUtils commonUtils;
private static final Logger logger = Logger.getLogger(HunterDAOImpl.class) ;
#Override
public Integer insert1() throws SQLException {
Integer insertNumRows = 0;
Connection connectionObject = transManager.getDataSource().getConnection();
PreparedStatement psObject = connectionObject.prepareStatement(
SQLQuery );
insertNumRows = psObject.executeUpdate();
commonUtils.closer(null,psObject,connectionObject);
// Function to close open connection resultsets statement objects
return insertNumRows;
}
#Override
public Integer insert2() throws SQLException {
Integer insertNumRows = 0;
Connection connectionObject = transManager.getDataSource().getConnection();
PreparedStatement psObject = connectionObject.prepareStatement(
SQLQuery );
insertNumRows = psObject.executeUpdate();
commonUtils.closer(null,psObject,connectionObject);
// Function to close open connection resultsets statement objects
return insertNumRows;
}
}
AppConfig.java
import com.interceptors.AppInterceptor;
import com.utils.Constants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.*;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
#Component
#Configuration
#EnableWebMvc
#Configurable
#ComponentScan(basePackages = {Constants.BASE_PACKAGE})
#EnableAspectJAutoProxy(proxyTargetClass = true)
#PropertySource(Constants.DB_PROPERTIES_PATH)
#EnableTransactionManagement
public class AppConfig extends WebMvcConfigurerAdapter implements EnvironmentAware{
#Autowired
Environment environmentObject;
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(){
PropertySourcesPlaceholderConfigurer placeHolder =new PropertySourcesPlaceholderConfigurer();
placeHolder.setLocation(new ClassPathResource(Constants.PROP_FILE_NAME));
return placeHolder;
}
#Bean
public InternalResourceViewResolver viewResolver(){
InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver();
internalResourceViewResolver.setPrefix(Constants.ROOT_PATH);
internalResourceViewResolver.setSuffix(Constants.JSP_DOT_EXTENSION);
return internalResourceViewResolver;
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Bean
public DriverManagerDataSource dataSource(){
DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
driverManagerDataSource.setDriverClassName(environmentObject.getProperty(Constants.JDBC_DRIVERCLASS_NAME_PROP_FILE));
driverManagerDataSource.setUrl(environmentObject.getProperty(Constants.JDBC_URL_PROP_FILE));
driverManagerDataSource.setUsername(environmentObject.getProperty(Constants.JDBC_USERNAME_PROP_FILE));
driverManagerDataSource.setPassword(new String(Constants.PASSWORD));
return driverManagerDataSource;
}
#Bean
public AutowiredAnnotationBeanPostProcessor postProcessorBean(){
return new AutowiredAnnotationBeanPostProcessor();
}
private ClientHttpRequestFactory getClientHttpRequestFactory() {
int timeout = 5000;
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
clientHttpRequestFactory.setConnectTimeout(timeout);
return clientHttpRequestFactory;
}
#Bean
public DataSourceTransactionManager transactionManager(){
final DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource());
transactionManager.setRollbackOnCommitFailure(true);
return transactionManager;
}
}
And I also have an around advice for my project:
#Aspect
#Component
public class AroundAdvice
{
private static final Logger logger = Logger.getLogger(AroundAdvice.class) ;
// Add pointcuts here - package names here for around advice
#Around("execution(* com.beans.*.*(..)) || " +
"execution(* com.config.*.*(..)) || " +
"execution(* com.controller.*.*(..)) || " +
"execution(* com.dao.*.*(..)) || " +
"execution(* com.service.*.*(..)) || " +
"execution(* com.utils.*.*(..)) || "+
"execution(* com.interceptors.*.*(..))")
public Object aroundAdviceMethod(ProceedingJoinPoint proceedingObject)throws Throwable{
MethodSignature methodSignObject = (MethodSignature) proceedingObject.getSignature();
logger.debug(Constants.EXECUTING_METH_STR + methodSignObject.getMethod());
Object value = null;
try {
value = proceedingObject.proceed();
} catch (Exception e) {
e.printStackTrace();
logger.error(Constants.EXCEPTION_ASPECT_STR+methodSignObject.getMethod());
logger.error(Constants.EXCEPTION_MESSAGE,e);
throw e;
}
logger.debug(Constants.RETURN_STR+value);
return value;
}
}
On executing this flow, the inserts are successful, however when the exception is thrown, it is not rolling back. However, my logger reads that rolling back is initialized and done as follows,
14:11:51 DEBUG DataSourceTransactionManager:851 - Initiating transaction rollback
14:11:51 DEBUG DataSourceTransactionManager:325 - Rolling back JDBC transaction on Connection [org.postgresql.jdbc4.Jdbc4Connection#3b467b21]
14:11:51 DEBUG DataSourceTransactionManager:368 - Releasing JDBC Connection [org.postgresql.jdbc4.Jdbc4Connection#3b467b21] after transaction
Please let me know if I am missing something
The problem is your dao. You are opening connections yourself and therefor bypass all transaction management. Your dao is to complex just use a JdbcTemplate instead of your current code.
#Repository
public class DAOImpl implements DAO {
private static final Logger logger = Logger.getLogger(HunterDAOImpl.class) ;
private final JdbcTemplate jdbc;
public DAOImpl(DataSource dataSource) {
this.jdbc = new JdbcTemplate(dataSource);
}
#Override
public Integer insert1() throws SQLException {
return jdbc.update(SQLQuery);
}
#Override
public Integer insert2() throws SQLException {
return jdbc.update(SQLQuery);
}
}
This will do exactly the same as your code, with one main difference it will use the Connection opened when the transaction was started. Your sample used 3 separate connections and thus 3 individual transactions instead of 1 single transaction.