I'm trying to understand Spring Java based configuration. Typically I might have an XML configuration containing something like:
<context:property-placeholder location="jdbc.properties"/>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
p:driverClassName="${jdbc.driverClassName}"
p:ur="${jdbc.url}"
p:username="${jdbc.username}"
p:password="${jdbc.password}"/>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
p:dataSource-ref="dataSource" />
My effort to produce an equivalent but using Spring Java based configuration has stalled. I'm not sure what to do when trying to inject my dataSource into a jdbcTemplate:
#PropertySource("classpath:jdbc.properties")
#Configuration
public class Config {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean(name = "dataSource")
public DataSource dataSource(
#Value("${jdbc.driverClassName}") final String driverClassName,
#Value("${jdbc.url}") final String url,
#Value("${jdbc.username}") final String username,
#Value("${jdbc.password}") final String password) {
BasicDataSource datasource = new BasicDataSource();
datasource.setDriverClassName(driverClassName);
datasource.setUrl(url);
datasource.setUsername(username);
datasource.setPassword(password);
return datasource;
}
#Bean(name = "jdbcTemplate")
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(
/* Property 'dataSource' is required and needs to be referenced
* What goes here?
*/
);
}
}
My guess is there isn't a direct equivalent and I may need to go about things in a subtly different way?
As JdbcTemplate contains JdbcTemplate(DataSource dataSource) constructor.
You can try this way
#PropertySource("classpath:jdbc.properties")
#Configuration
public class Config {
#Autowired
private Environment env;
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean(name = "dataSource")
public DataSource dataSource() {
BasicDataSource datasource = new BasicDataSource();
datasource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
// Similarly other values
// datasource.setUrl(url);
// datasource.setUsername(username);
// datasource.setPassword(password);
return datasource;
}
#Bean(name = "jdbcTemplate")
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource());
}
}
You don't need to create jdbcTemplate in the configuration. You could create it in the DAO class.
In the DAO Class:
private JdbcTemplate jdbcTemplate;
#Autowired
public void setDataSource(final DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
}
Related
I'm using SpringBoot 2.1.8.RELEASE, I have two datasource, I manually control transactions.
transaction (marked"#Primary") works. But, transaction marked another datasource is not working properly.
Do you have any suggestions?
Maven configuration is as below
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
other omit...
One datasource configure as below:
#Configuration
#MapperScan(basePackages = {"com.XXX.XXXX.mapper","com.XXX.XXX.common.mapper"},sqlSessionFactoryRef = "masterSqlSessionFactory")
public class ProductDataSourceConfig {
static final String MAPPER_LOCATION = "classpath:mybatis/MYSQL/*.xml";
#Bean(name = "masterDataSource")
#ConfigurationProperties("spring.datasource.product")
public DataSource masterDataSource(){
return DataSourceBuilder.create().build();
}
#Bean(name = "masterTransactionManager")
public DataSourceTransactionManager masterTransactionManager() {
return new DataSourceTransactionManager(masterDataSource());
}
#Bean(name = "masterSqlSessionFactory")
public SqlSessionFactory masterSqlSessionFactory(#Qualifier("masterDataSource") DataSource masterDataSource)
throws Exception {
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(masterDataSource);
sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources(ProductDataSourceConfig.MAPPER_LOCATION));
return sessionFactory.getObject();
}
}
another datasource configure as below:
#Configuration
#MapperScan(basePackages = "com.XXX.XXX.mapperTest",sqlSessionFactoryRef = "secondSqlSessionFactory")
public class TestDataSourceConfig {
static final String MAPPER_LOCATION = "classpath:mybatis/TESTMYSQL/*.xml";
#Primary
#Bean(name = "secondDataSource")
#ConfigurationProperties("spring.datasource.test")
public DataSource clusterDataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = "secondTransactionManager")
public DataSourceTransactionManager clusterTransactionManager() {
return new DataSourceTransactionManager(clusterDataSource());
}
#Primary
#Bean(name = "secondSqlSessionFactory")
public SqlSessionFactory clusterSqlSessionFactory(#Qualifier("secondDataSource") DataSource clusterDataSource)
throws Exception {
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(clusterDataSource);
sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources(TestDataSourceConfig.MAPPER_LOCATION));
return sessionFactory.getObject();
}
}
When configured as follows, submit as expected(in the "#Primary" db,record inserted ).
#Autowired
DataSourceTransactionManager dataSourceTransactionManager;
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setTimeout(500);
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(def);
insertTmemberEnterRecord(bean);
dataSourceTransactionManager.commit(transactionStatus);
But the following configuration as follows, submit is not work(No data inserted into table),also no error message.
#Qualifier("masterTransactionManager")
#Autowired
DataSourceTransactionManager dataSourceTransactionManager;
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setTimeout(500);
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(def);
insertTmemberEnterRecord(bean);
dataSourceTransactionManager.commit(transactionStatus);
Any suggestions are welcome.
I solved the problem.
The reason is that the corresponding mapper file should also be used.
I want to configure Spring Boot to use 2 JNDI datasources. I tried this configuration:
application.properties
spring.production.datasource.jndi-name=java:/global/production_gateway
spring.production.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.production.datasource.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect
spring.production.datasource.jpa.show-sql = true
spring.production.datasource.jpa.hibernate.ddl-auto = update
spring.warehouse.datasource.jndi-name=java:/global/production_warehouse
spring.warehouse.datasource.driver-class-name=org.mariadb.jdbc.Driver
spring.warehouse.datasource.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect
spring.warehouse.datasource.jpa.show-sql = true
spring.warehouse.datasource.jpa.hibernate.ddl-auto = update
primary database
#Configuration
#EnableJpaRepositories(
basePackages = "org.datalis.plugin.production.entity",
entityManagerFactoryRef = "productionEntityManagerFactory",
transactionManagerRef = "productionTransactionManager"
)
#EnableTransactionManagement
public class ContextProductionDatasource {
#Primary
#Bean(name = "productionDataSourceProperties")
#ConfigurationProperties(prefix="spring.production.datasource")
public JndiPropertyHolder productionDataSourceProperties() {
return new JndiPropertyHolder();
}
#Primary
#Bean(name = "productionDataSource")
#ConfigurationProperties(prefix="spring.production.datasource")
public DataSource productionDataSource() {
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
DataSource dataSource = dataSourceLookup.getDataSource(productionDataSourceProperties().getJndiName());
return dataSource;
}
#Primary
#Bean(name = "productionEntityManager")
public EntityManager productionEntityManager(EntityManagerFactory emf) {
return emf.createEntityManager();
}
#Primary
#Bean(name = "productionEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean productionEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("hibernate.hbm2ddl.auto", "update");
return builder
.dataSource(productionDataSource())
.packages("org.datalis.plugin.production.entity")
.persistenceUnit("production")
.properties(properties)
.build();
}
#Primary
#Bean(name = "productionTransactionManager")
public PlatformTransactionManager productionTransactionManager(final EntityManagerFactory emf) {
final JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
#Primary
#Bean(name = "productionExceptionTranslation")
public PersistenceExceptionTranslationPostProcessor productionExceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
private static class JndiPropertyHolder {
private String jndiName;
public String getJndiName() {
return jndiName;
}
public void setJndiName(String jndiName) {
this.jndiName = jndiName;
}
}
}
second datasource:
#Configuration
#EnableJpaRepositories(
basePackages = "org.datalis.plugin.warehouse.entity",
entityManagerFactoryRef = "warehouseEntityManagerFactory",
transactionManagerRef = "warehouseTransactionManager"
)
#EnableTransactionManagement
public class ContextWarehouseDatasource {
#Bean(name = "warehouseDataSourceProperties")
#ConfigurationProperties(prefix="spring.warehouse.datasource")
public JndiPropertyHolder warehouseDataSourceProperties() {
return new JndiPropertyHolder();
}
#Bean(name = "warehouseDataSource")
#ConfigurationProperties(prefix="spring.warehouse.datasource")
public DataSource warehouseDataSource() {
JndiDataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
DataSource dataSource = dataSourceLookup.getDataSource(warehouseDataSourceProperties().getJndiName());
return dataSource;
}
#Bean(name = "warehouseEntityManager")
public EntityManager warehouseEntityManager(EntityManagerFactory emf) {
return emf.createEntityManager();
}
#Bean(name = "warehouseEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean warehouseEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("hibernate.hbm2ddl.auto", "update");
return builder
.dataSource(warehouseDataSource())
.packages("org.datalis.plugin.warehouse.entity")
.persistenceUnit("warehouse")
.properties(properties)
.build();
}
#Bean(name = "warehouseTransactionManager")
public PlatformTransactionManager warehouseTransactionManager(final EntityManagerFactory emf) {
final JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
#Bean(name = "warehouseExceptionTranslation")
public PersistenceExceptionTranslationPostProcessor warehouseExceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
private static class JndiPropertyHolder {
private String jndiName;
public String getJndiName() {
return jndiName;
}
public void setJndiName(String jndiName) {
this.jndiName = jndiName;
}
}
}
When I deploy the code I get exception:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' available
Full error stack:
https://pastebin.com/bBZPZGfu
Do you know how I can solve this issue?
When I remove:
#Primary
#Bean(name = "productionEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean productionEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("hibernate.hbm2ddl.auto", "update");
return builder
.dataSource(productionDataSource())
.packages("org.datalis.plugin.production.entity")
.persistenceUnit("production")
.properties(properties)
.build();
}
The package is properly deployed. Any idea why?
The main problem is to have 2 different Entity managers, which access different databases.
So the cause of the exception: Spring Data JPA tries to create a set of repositories but does not know which entity manager factory to use. By default, Spring Data JPA expects only one Entity manager factory bean, preferably named entityManagerFactory, but you do not have such.
So you have to be really precise in configuration: for example, you can organize your code in 2 packages: ...warehouse.* and app.production.*, then you can specify a precise configuration of Spring Data JPA: #EnableJpaRepositories(basePackages = "...warehouse.**", entityManagerFactoryRef = "warehouseEntityManagerFactory") and for production #EnableJpaRepositories(basePackages = "...production.**", entityManagerFactoryRef = "productionEntityManagerFactory").
The second step is to ensure that no default Data JPA instantiation is done: adding the configuration property spring.data.jpa.repositories.enabled=false will resolve this.
And looking through the configuration disable any kind of other #EnableJpaRepositories or #EntityScan except defined above precise configurations.
And during creation of LocalContainerEntityManagerFactoryBean do not use injected EntityManagerFactoryBuilder: dead simple new LocalContainerEntityManagerFactoryBean() will work better.
And last but not least, related to application in general: you have to think about 2-phase commit transactions: you have 2 data sources, which can be accessed within single transactions but each of them is managed by different transaction managers.
Put annotation #ConfigurationProperties("spring.datasource") on you datasource definitions, e.g. warehouseDataSource() and productionDataSource()
Eek! I'd advise following the database per microservice pattern and changing your solution/architecture to have one microservice with a production micro db, and one microservice with a warehouse micro db!
I'm following an example in a Spring book to create a little web store application.
The example uses the hsqldb embedded Database, but I don't want to use it.
I want to connect to a MySQL database and, later, use the Hibernate sessionFractory.
I edited the example code this way:
#Configuration
#ComponentScan("com.packagename.webstore")
public class RootApplicationContextConfig {
#Bean
public DataSource dataSource() {
// this is the original code of the example
/*EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase db = builder
.setType(EmbeddedDatabaseType.HSQL)
.addScript("db/sql/create-table.sql")
.addScript("db/sql/insert-data.sql")
.build();
return db; */
//this is my code
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/dbName");
dataSource.setUsername( "user" );
dataSource.setPassword( "pass" );
return dataSource;
}
}
Then, in my classes I access the datasource this way:
#Autowired
private DataSource datasource;
...
Connection connection = datasource.getConnection();
...
Is it correct what I did?
If later I want to use the Hibernate sessionFactory, how should I edit my code?
Thank you guys
If you want configure SessionFactory with spring then to create required beans like below
#Configuration
#EnableTransactionManagement
#PropertySource({ "classpath:persistence-mysql.properties" })
#ComponentScan({ "org.baeldung.spring.persistence" })
public class PersistenceConfig {
#Autowired
private Environment env;
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(restDataSource());
sessionFactory.setPackagesToScan(
new String[] { "entity class package name so it can scan them" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
}
#Bean
public DataSource restDataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
dataSource.setUrl(env.getProperty("jdbc.url"));
dataSource.setUsername(env.getProperty("jdbc.user"));
dataSource.setPassword(env.getProperty("jdbc.pass"));
return dataSource;
}
#Bean
#Autowired
public HibernateTransactionManager transactionManager(
SessionFactory sessionFactory) {
HibernateTransactionManager txManager
= new HibernateTransactionManager();
txManager.setSessionFactory(sessionFactory);
return txManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
Properties hibernateProperties() {
return new Properties() {
{
setProperty("hibernate.hbm2ddl.auto",
env.getProperty("hibernate.hbm2ddl.auto"));
setProperty("hibernate.dialect",
env.getProperty("hibernate.dialect"));
setProperty("hibernate.globally_quoted_identifiers",
"true");
}
};
}
}
property file
# jdbc confi
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_hibernate_dev?createDatabaseIfNotExist=true
jdbc.user=tutorialuser
jdbc.pass=tutorialmy5ql
# hibernate config
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
hibernate.show_sql=false
hibernate.hbm2ddl.auto=create-drop
then you can you and Datasource or SessionFactory in your required class
public class HibernateDAO{
#Autowired
SessionFactory sessionFactory;
#Autowired
DataSource datasource;
}
Here is example link
I'm trying to retrieve data from application.properties file using Environment in my Spring Application but it's not working. I cant get the data bound correctly by Environment. I only can get this working if I use local variables as shown below:
AppConfig.class now!
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories("com.victommasi.eshop.dao")
#PropertySource("classpath:application.properties")
public class AppConfig {
private static final String driverClass = "com.mysql.jdbc.Driver";
private static final String url = "jdbc:mysql://localhost/eshop";
private static final String username = "root";
private static final String password = "root";
private static final String dialect = "org.hibernate.dialect.MySQL5Dialect";
private static final String showSql = "true";
private static final String formatSql = "true";
private static final String hbm2dllAuto = "update";
private static final String packageToScan = "com.victommasi.eshop.model";
#Bean
public DataSource dataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driverClass);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", dialect);
properties.put("hibernate.show_sql", showSql);
properties.put("hibernate.format_sql", formatSql);
properties.put("hibernate.hbm2ddl.auto", hbm2dllAuto);
return properties;
}
#Bean
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPersistenceProvider(new HibernatePersistenceProvider());
entityManagerFactoryBean.setPackagesToScan(packageToScan);
entityManagerFactoryBean.setJpaProperties(hibernateProperties());
return entityManagerFactoryBean;
}
}
AppConfig.class as I want
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories("com.victommasi.eshop.dao")
#PropertySource("classpath:application.properties")
public class AppConfig {
#Autowired
private Environment env;
#Bean
public DataSource dataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("jdbc.driverClass"));
dataSource.setUrl(env.getProperty("jdbc.url"));
dataSource.setUsername(env.getProperty("jdbc.username"));
dataSource.setPassword(env.getProperty("jdbc.password"));
return dataSource;
}
private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", env.getProperty("hibernate.dialect"));
properties.put("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
properties.put("hibernate.format_sql", env.getProperty("hibernate.format_sql"));
properties.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
return properties;
}
#Bean
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPersistenceProvider(new HibernatePersistenceProvider());
entityManagerFactoryBean.setPackagesToScan(env.getProperty("packages.to.scan"));
entityManagerFactoryBean.setJpaProperties(hibernateProperties());
return entityManagerFactoryBean;
}
Other classes:
WebConfig.class
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = { "com.victommasi.eshop" })
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Bean
public InternalResourceViewResolver internalResourceViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setContentType("text/html;charset=UTF-8");
resolver.setSuffix(".jsp");
return resolver;
}
#Bean(name = "filterMultipartResolver")
public CommonsMultipartResolver getMultipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(1048576);
multipartResolver.setMaxInMemorySize(1048576);
return multipartResolver;
}
}
WebAppInitializer.class
public class WebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) throws ServletException {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(WebConfig.class, AppConfig.class, SecurityConfig.class);
container.addListener(new ContextLoaderListener(rootContext));
AnnotationConfigWebApplicationContext dispatcherServlet = new AnnotationConfigWebApplicationContext();
dispatcherServlet.register(WebConfig.class);
ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet(dispatcherServlet));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
I know it seems to be a very easy, I have also followed this tutorial, but cant get this done.
I didn't go through it all but it seems like you're missing the PropertySourcesPlaceholderConfigurer bean
Since Spring 3.1 introduced the new #PropertySource annotation, as a
convenient mechanism of adding property sources to the environment.
This annotation is to be used in conjunction with Java based
configuration and the #Configuration annotation:
#Configuration
#PropertySource("classpath:application.properties")
public class AppConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
With that, you can now start injecting a property with the #Value annotation is straightforward:
#Value( "${jdbc.url}" )
private String jdbcUrl;
On the other note, consider/look into spring-boot , you'll get all the above (including all the code you have shared)and much more out for free i.e with zero line of code
After reading a comment I took a look at console and found that data in 'application.properties' file were binded to AppConfig.class by Environment with blank spaces.
Stacktrace:
Caused by: java.sql.SQLException: Access denied for user 'root '#'localhost'
I assume it was the reason the binding wasn't working. My application is now working as I wanted. Thanks.
How can I configure and use two data sources?
For example, here is what I have for the first data source:
application.properties
#first db
spring.datasource.url = [url]
spring.datasource.username = [username]
spring.datasource.password = [password]
spring.datasource.driverClassName = oracle.jdbc.OracleDriver
#second db ...
Application class
#SpringBootApplication
public class SampleApplication
{
public static void main(String[] args) {
SpringApplication.run(SampleApplication.class, args);
}
}
How do I modify application.properties to add another data source? How do I autowire it to be used by a different repository?
Here you go.
Add in your application.properties file:
#first db
spring.datasource.url = [url]
spring.datasource.username = [username]
spring.datasource.password = [password]
spring.datasource.driverClassName = oracle.jdbc.OracleDriver
#second db ...
spring.secondDatasource.url = [url]
spring.secondDatasource.username = [username]
spring.secondDatasource.password = [password]
spring.secondDatasource.driverClassName = oracle.jdbc.OracleDriver
Add in any class annotated with #Configuration the following methods:
#Bean
#Primary
#ConfigurationProperties(prefix="spring.datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
#ConfigurationProperties(prefix="spring.secondDatasource")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
Update 2022-05-29 with Spring Boot 1.5.8.RELEASE which should work with Spring Boot 2.x
Most answers do not provide how to use them (as datasource itself and as transaction), only how to config them.
Moreover you should know how to commit/rollback transactions of both datasources at the same time.
You can see the runnable example and some explanation in https://github.com/surasint/surasint-examples/tree/master/spring-boot-jdbi/10_spring-boot-two-databases (see what you can try in README.txt)
I copied some code here.
First you have to set application.properties like this
#Database
database1.datasource.url=jdbc:mysql://localhost/testdb
database1.datasource.username=root
database1.datasource.password=root
database1.datasource.driver-class-name=com.mysql.jdbc.Driver
database2.datasource.url=jdbc:mysql://localhost/testdb2
database2.datasource.username=root
database2.datasource.password=root
database2.datasource.driver-class-name=com.mysql.jdbc.Driver
Then define them as providers (#Bean) like this:
#Bean(name = "datasource1")
#ConfigurationProperties("database1.datasource")
#Primary
public DataSource dataSource(){
return DataSourceBuilder.create().build();
}
#Bean(name = "datasource2")
#ConfigurationProperties("database2.datasource")
public DataSource dataSource2(){
return DataSourceBuilder.create().build();
}
Note that I have #Bean(name="datasource1") and #Bean(name="datasource2"), then you can use it when we need datasource as #Qualifier("datasource1") and #Qualifier("datasource2") , for example
#Qualifier("datasource1")
#Autowired
private DataSource dataSource;
If you do care about transaction, you have to define DataSourceTransactionManager for both of them, like this:
#Bean(name="tm1")
#Autowired
#Primary
DataSourceTransactionManager tm1(#Qualifier ("datasource1") DataSource datasource) {
DataSourceTransactionManager txm = new DataSourceTransactionManager(datasource);
return txm;
}
#Bean(name="tm2")
#Autowired
DataSourceTransactionManager tm2(#Qualifier ("datasource2") DataSource datasource) {
DataSourceTransactionManager txm = new DataSourceTransactionManager(datasource);
return txm;
}
Then you can use it like
#Transactional //this will use the first datasource because it is #primary
or
#Transactional("tm2")
The most important part, which you will hardly find an example in anywhere: if you want a method to commit/rollback transactions of both databases, you need ChainedTransactionManager for tm1 and tm2 , like this:
#Bean(name = "chainedTransactionManager")
public ChainedTransactionManager getChainedTransactionManager(#Qualifier ("tm1") DataSourceTransactionManager tm1, #Qualifier ("tm2") DataSourceTransactionManager tm2){
return new ChainedTransactionManager(tm1, tm2);
}
To use it, add this annotation in a method #Transactional(value="chainedTransactionManager") for example
#Transactional(value="chainedTransactionManager")
public void insertAll() {
UserBean test = new UserBean();
test.setUsername("username" + new Date().getTime());
userDao.insert(test);
userDao2.insert(test);
}
This should be enough. See example and detail in the link above.
Refer the official documentation
Creating more than one data source works same as creating the first one. You might want to mark one of them as #Primary if you are using the default auto-configuration for JDBC or JPA (then that one will be picked up by any #Autowired injections).
#Bean
#Primary
#ConfigurationProperties(prefix="datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
#ConfigurationProperties(prefix="datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
I also had to setup connection to 2 datasources from Spring Boot application, and it was not easy - the solution mentioned in the Spring Boot documentation didn't work. After a long digging through the internet I made it work and the main idea was taken from this article and bunch of other places.
The following solution is written in Kotlin and works with Spring Boot 2.1.3 and Hibernate Core 5.3.7. Main issue was that it was not enough just to setup different DataSource configs, but it was also necessary to configure EntityManagerFactory and TransactionManager for both databases.
Here is config for the first (Primary) database:
#Configuration
#EnableJpaRepositories(
entityManagerFactoryRef = "firstDbEntityManagerFactory",
transactionManagerRef = "firstDbTransactionManager",
basePackages = ["org.path.to.firstDb.domain"]
)
#EnableTransactionManagement
class FirstDbConfig {
#Bean
#Primary
#ConfigurationProperties(prefix = "spring.datasource.firstDb")
fun firstDbDataSource(): DataSource {
return DataSourceBuilder.create().build()
}
#Primary
#Bean(name = ["firstDbEntityManagerFactory"])
fun firstDbEntityManagerFactory(
builder: EntityManagerFactoryBuilder,
#Qualifier("firstDbDataSource") dataSource: DataSource
): LocalContainerEntityManagerFactoryBean {
return builder
.dataSource(dataSource)
.packages(SomeEntity::class.java)
.persistenceUnit("firstDb")
// Following is the optional configuration for naming strategy
.properties(
singletonMap(
"hibernate.naming.physical-strategy",
"org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl"
)
)
.build()
}
#Primary
#Bean(name = ["firstDbTransactionManager"])
fun firstDbTransactionManager(
#Qualifier("firstDbEntityManagerFactory") firstDbEntityManagerFactory: EntityManagerFactory
): PlatformTransactionManager {
return JpaTransactionManager(firstDbEntityManagerFactory)
}
}
And this is config for second database:
#Configuration
#EnableJpaRepositories(
entityManagerFactoryRef = "secondDbEntityManagerFactory",
transactionManagerRef = "secondDbTransactionManager",
basePackages = ["org.path.to.secondDb.domain"]
)
#EnableTransactionManagement
class SecondDbConfig {
#Bean
#ConfigurationProperties("spring.datasource.secondDb")
fun secondDbDataSource(): DataSource {
return DataSourceBuilder.create().build()
}
#Bean(name = ["secondDbEntityManagerFactory"])
fun secondDbEntityManagerFactory(
builder: EntityManagerFactoryBuilder,
#Qualifier("secondDbDataSource") dataSource: DataSource
): LocalContainerEntityManagerFactoryBean {
return builder
.dataSource(dataSource)
.packages(EntityFromSecondDb::class.java)
.persistenceUnit("secondDb")
.build()
}
#Bean(name = ["secondDbTransactionManager"])
fun secondDbTransactionManager(
#Qualifier("secondDbEntityManagerFactory") secondDbEntityManagerFactory: EntityManagerFactory
): PlatformTransactionManager {
return JpaTransactionManager(secondDbEntityManagerFactory)
}
}
The properties for datasources are like this:
spring.datasource.firstDb.jdbc-url=
spring.datasource.firstDb.username=
spring.datasource.firstDb.password=
spring.datasource.secondDb.jdbc-url=
spring.datasource.secondDb.username=
spring.datasource.secondDb.password=
Issue with properties was that I had to define jdbc-url instead of url because otherwise I had an exception.
p.s.
Also you might have different naming schemes in your databases, which was the case for me. Since Hibernate 5 does not support all previous naming schemes, I had to use solution from this answer - maybe it will also help someone as well.
Here is the Complete solution
#First Datasource (DB1)
db1.datasource.url: url
db1.datasource.username:user
db1.datasource.password:password
#Second Datasource (DB2)
db2.datasource.url:url
db2.datasource.username:user
db2.datasource.password:password
Since we are going to get access two different databases (db1, db2), we need to configure each data source configuration separately like:
public class DB1_DataSource {
#Autowired
private Environment env;
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean db1EntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(db1Datasource());
em.setPersistenceUnitName("db1EntityManager");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<string, object=""> properties = new HashMap<>();
properties.put("hibernate.dialect",
env.getProperty("hibernate.dialect"));
properties.put("hibernate.show-sql",
env.getProperty("jdbc.show-sql"));
em.setJpaPropertyMap(properties);
return em;
}
#Primary
#Bean
public DataSource db1Datasource() {
DriverManagerDataSource dataSource
= new DriverManagerDataSource();
dataSource.setDriverClassName(
env.getProperty("jdbc.driver-class-name"));
dataSource.setUrl(env.getProperty("db1.datasource.url"));
dataSource.setUsername(env.getProperty("db1.datasource.username"));
dataSource.setPassword(env.getProperty("db1.datasource.password"));
return dataSource;
}
#Primary
#Bean
public PlatformTransactionManager db1TransactionManager() {
JpaTransactionManager transactionManager
= new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
db1EntityManager().getObject());
return transactionManager;
}
}
Second Datasource :
public class DB2_DataSource {
#Autowired
private Environment env;
#Bean
public LocalContainerEntityManagerFactoryBean db2EntityManager() {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
em.setDataSource(db2Datasource());
em.setPersistenceUnitName("db2EntityManager");
HibernateJpaVendorAdapter vendorAdapter
= new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
HashMap<string, object=""> properties = new HashMap<>();
properties.put("hibernate.dialect",
env.getProperty("hibernate.dialect"));
properties.put("hibernate.show-sql",
env.getProperty("jdbc.show-sql"));
em.setJpaPropertyMap(properties);
return em;
}
#Bean
public DataSource db2Datasource() {
DriverManagerDataSource dataSource
= new DriverManagerDataSource();
dataSource.setDriverClassName(
env.getProperty("jdbc.driver-class-name"));
dataSource.setUrl(env.getProperty("db2.datasource.url"));
dataSource.setUsername(env.getProperty("db2.datasource.username"));
dataSource.setPassword(env.getProperty("db2.datasource.password"));
return dataSource;
}
#Bean
public PlatformTransactionManager db2TransactionManager() {
JpaTransactionManager transactionManager
= new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
db2EntityManager().getObject());
return transactionManager;
}
}
Here you can find the complete Example on my blog :
Spring Boot with Multiple DataSource Configuration
# Here '1stDB' is the database name
spring.datasource.url=jdbc:mysql://localhost/A
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
# Here '2ndDB' is the database name
spring.second-datasourcee.url=jdbc:mysql://localhost/B
spring.second-datasource.username=root
spring.second-datasource.password=root
spring.second-datasource.driver-class-name=com.mysql.jdbc.Driver
#Bean
#Primary
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource firstDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
#ConfigurationProperties(prefix = "spring.second-datasource")
public DataSource secondDataSource() {
return DataSourceBuilder.create().build();
}
My requirement was slightly different but used two data sources.
I have used two data sources for same JPA entities from same package. One for executing DDL at the server startup to create/update tables and another one is for DML at runtime.
The DDL connection should be closed after DDL statements are executed, to prevent further usage of super user previlleges anywhere in the code.
Properties
spring.datasource.url=jdbc:postgresql://Host:port
ddl.user=ddluser
ddl.password=ddlpassword
dml.user=dmluser
dml.password=dmlpassword
spring.datasource.driver-class-name=org.postgresql.Driver
Data source config classes
//1st Config class for DDL Data source
public class DatabaseDDLConfig {
#Bean
public LocalContainerEntityManagerFactoryBean ddlEntityManagerFactoryBean() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
PersistenceProvider persistenceProvider = new
org.hibernate.jpa.HibernatePersistenceProvider();
entityManagerFactoryBean.setDataSource(ddlDataSource());
entityManagerFactoryBean.setPackagesToScan(new String[] {
"com.test.two.data.sources"});
HibernateJpaVendorAdapter vendorAdapter = new
HibernateJpaVendorAdapter();
entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter);
HashMap<String, Object> properties = new HashMap<>();
properties.put("hibernate.dialect",
"org.hibernate.dialect.PostgreSQLDialect");
properties.put("hibernate.physical_naming_strategy",
"org.springframework.boot.orm.jpa.hibernate.
SpringPhysicalNamingStrategy");
properties.put("hibernate.implicit_naming_strategy",
"org.springframework.boot.orm.jpa.hibernate.
SpringImplicitNamingStrategy");
properties.put("hibernate.hbm2ddl.auto", "update");
entityManagerFactoryBean.setJpaPropertyMap(properties);
entityManagerFactoryBean.setPersistenceUnitName("ddl.config");
entityManagerFactoryBean.setPersistenceProvider(persistenceProvider);
return entityManagerFactoryBean;
}
#Bean
public DataSource ddlDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("spring.datasource.driver-class-name"));
dataSource.setUrl(env.getProperty("spring.datasource.url"));
dataSource.setUsername(env.getProperty("ddl.user");
dataSource.setPassword(env.getProperty("ddl.password"));
return dataSource;
}
#Bean
public PlatformTransactionManager ddlTransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(ddlEntityManagerFactoryBean().getObject());
return transactionManager;
}
}
//2nd Config class for DML Data source
public class DatabaseDMLConfig {
#Bean
#Primary
public LocalContainerEntityManagerFactoryBean dmlEntityManagerFactoryBean() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
PersistenceProvider persistenceProvider = new org.hibernate.jpa.HibernatePersistenceProvider();
entityManagerFactoryBean.setDataSource(dmlDataSource());
entityManagerFactoryBean.setPackagesToScan(new String[] { "com.test.two.data.sources" });
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter);
entityManagerFactoryBean.setJpaProperties(defineJpaProperties());
entityManagerFactoryBean.setPersistenceUnitName("dml.config");
entityManagerFactoryBean.setPersistenceProvider(persistenceProvider);
return entityManagerFactoryBean;
}
#Bean
#Primary
public DataSource dmlDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("spring.datasource.driver-class-name"));
dataSource.setUrl(envt.getProperty("spring.datasource.url"));
dataSource.setUsername("dml.user");
dataSource.setPassword("dml.password");
return dataSource;
}
#Bean
#Primary
public PlatformTransactionManager dmlTransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(dmlEntityManagerFactoryBean().getObject());
return transactionManager;
}
}
//Usage of DDL data sources in code.
public class DDLServiceAtStartup {
//Import persistence unit ddl.config for ddl purpose.
#PersistenceUnit(unitName = "ddl.config")
private EntityManagerFactory entityManagerFactory;
public void executeDDLQueries() throws ContentServiceSystemError {
try {
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
entityManager.createNativeQuery("query to create/update table").executeUpdate();
entityManager.flush();
entityManager.getTransaction().commit();
entityManager.close();
//Close the ddl data source to avoid from further use in code.
entityManagerFactory.close();
} catch(Exception ex) {}
}
//Usage of DML data source in code.
public class DDLServiceAtStartup {
#PersistenceUnit(unitName = "dml.config")
private EntityManagerFactory entityManagerFactory;
public void createRecord(User user) {
userDao.save(user);
}
}
#Primary annotation when used against a method like below works good if the two data sources are on the same db location/server.
#Bean(name = "datasource1")
#ConfigurationProperties("database1.datasource")
#Primary
public DataSource dataSource(){
return DataSourceBuilder.create().build();
}
#Bean(name = "datasource2")
#ConfigurationProperties("database2.datasource")
public DataSource dataSource2(){
return DataSourceBuilder.create().build();
}
If the data sources are on different servers its better to use #Component along with #Primary annotation. The following code snippet works well on two different data sources at different locations
database1.datasource.url = jdbc:mysql://127.0.0.1:3306/db1
database1.datasource.username = root
database1.datasource.password = mysql
database1.datasource.driver-class-name=com.mysql.jdbc.Driver
database2.datasource1.url = jdbc:mysql://192.168.113.51:3306/db2
database2.datasource1.username = root
database2.datasource1.password = mysql
database2.datasource1.driver-class-name=com.mysql.jdbc.Driver
#Configuration
#Primary
#Component
#ComponentScan("com.db1.bean")
class DBConfiguration1{
#Bean("db1Ds")
#ConfigurationProperties(prefix="database1.datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
}
#Configuration
#Component
#ComponentScan("com.db2.bean")
class DBConfiguration2{
#Bean("db2Ds")
#ConfigurationProperties(prefix="database2.datasource1")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
}
I used mybatis - springboot 2.0 tech stack,
solution:
//application.properties - start
sp.ds1.jdbc-url=jdbc:mysql://localhost:3306/mydb?useSSL=false
sp.ds1.username=user
sp.ds1.password=pwd
sp.ds1.testWhileIdle=true
sp.ds1.validationQuery=SELECT 1
sp.ds1.driverClassName=com.mysql.jdbc.Driver
sp.ds2.jdbc-url=jdbc:mysql://localhost:4586/mydb?useSSL=false
sp.ds2.username=user
sp.ds2.password=pwd
sp.ds2.testWhileIdle=true
sp.ds2.validationQuery=SELECT 1
sp.ds2.driverClassName=com.mysql.jdbc.Driver
//application.properties - end
//configuration class
#Configuration
#ComponentScan(basePackages = "com.mypkg")
public class MultipleDBConfig {
public static final String SQL_SESSION_FACTORY_NAME_1 = "sqlSessionFactory1";
public static final String SQL_SESSION_FACTORY_NAME_2 = "sqlSessionFactory2";
public static final String MAPPERS_PACKAGE_NAME_1 = "com.mypg.mymapper1";
public static final String MAPPERS_PACKAGE_NAME_2 = "com.mypg.mymapper2";
#Bean(name = "mysqlDb1")
#Primary
#ConfigurationProperties(prefix = "sp.ds1")
public DataSource dataSource1() {
System.out.println("db1 datasource");
return DataSourceBuilder.create().build();
}
#Bean(name = "mysqlDb2")
#ConfigurationProperties(prefix = "sp.ds2")
public DataSource dataSource2() {
System.out.println("db2 datasource");
return DataSourceBuilder.create().build();
}
#Bean(name = SQL_SESSION_FACTORY_NAME_1)
#Primary
public SqlSessionFactory sqlSessionFactory1(#Qualifier("mysqlDb1") DataSource dataSource1) throws Exception {
System.out.println("sqlSessionFactory1");
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setTypeHandlersPackage(MAPPERS_PACKAGE_NAME_1);
sqlSessionFactoryBean.setDataSource(dataSource1);
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBean.getObject();
sqlSessionFactory.getConfiguration().setMapUnderscoreToCamelCase(true);
sqlSessionFactory.getConfiguration().setJdbcTypeForNull(JdbcType.NULL);
return sqlSessionFactory;
}
#Bean(name = SQL_SESSION_FACTORY_NAME_2)
public SqlSessionFactory sqlSessionFactory2(#Qualifier("mysqlDb2") DataSource dataSource2) throws Exception {
System.out.println("sqlSessionFactory2");
SqlSessionFactoryBean diSqlSessionFactoryBean = new SqlSessionFactoryBean();
diSqlSessionFactoryBean.setTypeHandlersPackage(MAPPERS_PACKAGE_NAME_2);
diSqlSessionFactoryBean.setDataSource(dataSource2);
SqlSessionFactory sqlSessionFactory = diSqlSessionFactoryBean.getObject();
sqlSessionFactory.getConfiguration().setMapUnderscoreToCamelCase(true);
sqlSessionFactory.getConfiguration().setJdbcTypeForNull(JdbcType.NULL);
return sqlSessionFactory;
}
#Bean
#Primary
public MapperScannerConfigurer mapperScannerConfigurer1() {
System.out.println("mapperScannerConfigurer1");
MapperScannerConfigurer configurer = new MapperScannerConfigurer();
configurer.setBasePackage(MAPPERS_PACKAGE_NAME_1);
configurer.setSqlSessionFactoryBeanName(SQL_SESSION_FACTORY_NAME_1);
return configurer;
}
#Bean
public MapperScannerConfigurer mapperScannerConfigurer2() {
System.out.println("mapperScannerConfigurer2");
MapperScannerConfigurer configurer = new MapperScannerConfigurer();
configurer.setBasePackage(MAPPERS_PACKAGE_NAME_2);
configurer.setSqlSessionFactoryBeanName(SQL_SESSION_FACTORY_NAME_2);
return configurer;
}
}
Note :
1)#Primary -> #primary
2)---."jdbc-url" in properties -> After Spring Boot 2.0 migration: jdbcUrl is required with driverClassName
declaring a data source in Spring Boot application.properties
spring.datasource.company.url=jdbc:mysql://localhost/company_db?createDatabaseIfNotExist=true&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true
spring.datasource.company.username=root
spring.datasource.company.password=root
spring.datasource.company.platform=mysql
spring.datasource.employee.url=jdbc:mysql://localhost/employee_db?createDatabaseIfNotExist=true&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true
spring.datasource.employee.username=root
spring.datasource.employee.password=root
spring.datasource.employee.platform=mysql
use multiple data sources, we need to declare multiple beans with
different mappings within Spring's application context.
using a configuration class
#Configuration
#EnableJpaRepositories(basePackages = "com.example.multiple.datasources.entity.company",
entityManagerFactoryRef = "companyEntityManagerFactory",
transactionManagerRef = "companyTransactionManager")
public class CompanyDataSourceConfiguration {
#Bean
#ConfigurationProperties("spring.datasource.company")
public DataSourceProperties companyDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#ConfigurationProperties("spring.datasource.company.configuration")
public DataSource companyDataSource() {
return companyDataSourceProperties().initializeDataSourceBuilder()
.type(HikariDataSource.class).build();
}
#Bean(name = "companyEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean companyEntityManagerFactory(EntityManagerFactoryBuilder builder) {
return builder.dataSource(companyDataSource()).packages(Company.class).build();
}
#Bean
public PlatformTransactionManager companyTransactionManager(
final #Qualifier("companyEntityManagerFactory") LocalContainerEntityManagerFactoryBean companyEntityManagerFactory
) {
return new JpaTransactionManager(companyEntityManagerFactory.getObject());
}
}
we need to declare one of the datasources as #Primary. This is because
EntityManagerFactoryBuilder is declared in JpaBaseConfiguration and
this class need a single data source injected.
#Configuration
#EnableJpaRepositories(basePackages = "com.example.multiple.datasources.entity.employee",
entityManagerFactoryRef = "employeeEntityManagerFactory",
transactionManagerRef = "employeeTransactionManager")
public class EmployeeDatasourceConfiguration {
#Bean
#Primary
#ConfigurationProperties("spring.datasource.employee")
public DataSourceProperties employeeDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#Primary
#ConfigurationProperties("spring.datasource.employee.configuration")
public DataSource employeeDataSource() {
return employeeDataSourceProperties().initializeDataSourceBuilder().type(HikariDataSource.class).build();
}
#Primary
#Bean("employeeEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean employeeEntityManagerFactory(EntityManagerFactoryBuilder builder) {
return builder.dataSource(employeeDataSource()).packages(Employee.class).build();
}
#Primary
#Bean
public PlatformTransactionManager employeeTransactionManager(
final #Qualifier("employeeEntityManagerFactory") LocalContainerEntityManagerFactoryBean employeeEntityManagerFactory
) {
return new JpaTransactionManager(employeeEntityManagerFactory.getObject());
}
}