I have two different mysql database from the same connection and would like to use both in my application.properties like this way:
spring.datasource.url=jdbc:mysql://localhost:3306/membership
spring.datasource.username=root
spring.datasource.password=
spring.datasource.url=jdbc:mysql://localhost:3306/finance
spring.datasource.username=root
spring.datasource.password=
But this is not allowed (duplicate key's).
I have found this guide but this is to much code i think not a elegant solution, looks like workaround for simple problem.
Are there better and much simpler solutions?
PS: I am new to spring boot.
You have to decleare different keys for different datasource. The main config for multi datasource is(this is the demo):
#Bean
#Primary
#ConfigurationProperties("app.datasource.foo")
public DataSourceProperties fooDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#Primary
#ConfigurationProperties("app.datasource.foo")
public DataSource fooDataSource() {
return fooDataSourceProperties().initializeDataSourceBuilder().build();
}
#Bean
#ConfigurationProperties("app.datasource.bar")
public DataSourceProperties barDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#ConfigurationProperties("app.datasource.bar")
public DataSource barDataSource() {
return barDataSourceProperties().initializeDataSourceBuilder().build();
}
You can refer to this link and the post to have an overview for the configuration.
I think you can use only 1 MySQL database. Try to combine with PostgreSQL. Example
Related
I'm running h2 database with spring boot. Basic configuration
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=create
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
Everything runs smoothly.
Now I want to define another Datasource bean in my application
#Bean
#Qualifier("oracleDatasource")
public DataSource oracleDataSource(#Qualifier("oracleDBProperties") DataSourceProperties oracleDBProperties) {
return oracleDBProperties.initializeDataSourceBuilder().build();
}
#Bean
#ConfigurationProperties(prefix = "spring.oracle.datasource")
public DataSourceProperties oracleDBProperties() {
return new DataSourceProperties();
}
spring.oracle.datasource.url=jdbc:oracle:thin:url:db
spring.oracle.datasource.username=test
spring.oracle.datasource.password=test
spring.oracle.datasource.driver-class-name=oracle.jdbc.OracleDriver
The oracle datasource initializes successfully but this time h2 configuration is ignored. Looks like defining another DataSource bean suppresses the spring's autoconfiguration for h2.
How can I avoid that suppression or is there a way to define h2 same way as I did for oracle?
I have tried to do the same type of bean config for h2 as I've done for oracle but it doesn't seem to work.
UPDATE
Fixed the problem with following
#Bean
#Primary
#ConfigurationProperties(prefix = "spring.h2.datasource")
public DataSourceProperties h2DBProperties() {
return new DataSourceProperties();
}
#Bean
#Primary
#Qualifier("h2Datasource")
public DataSource h2DataSource(#Qualifier("h2DBProperties") DataSourceProperties h2DBProperties) {
return h2DBProperties.initializeDataSourceBuilder().build();
}
spring.h2.datasource.url=jdbc:h2:mem:testdb
spring.h2.datasource.driverClassName=org.h2.Driver
spring.h2.datasource.username=sa
spring.h2.datasource.password=
Adding the #Primary annotation didn't let anything suppress it. Although, still not sure to consider this as a right solution.
I have achieved the same thing some time before.
http://www.kode12.com/kode12/spring-boot/spring-boot-working-multiple-sql-database/
I'm developing a standalone application with following technology stack:
Spring Boot version 2.1.0.RELEASE
Oracle12c System with driver ojdbc6 (11.2.0.3)
Apache Camel
JPA for the main datasource
JDBC for a secondary datasource (read only)
The JPA datasource is the primary datasource where the application itself is connected to and write data to. The JDBC is an additional datasource to read data from a database with another purpose.
During runtime I encounter the following issue:
I poll/select a JPA Entity from the primary datasource and do some processing. This processing includes running a select query on the secondary datasource via a jdbc template. Now if the execution of the query throws an exception I am able to catch it and want to update a status field on the JPA Entity and write it to the datasource.
I've already read that Oracle tries to do a rollback if a SQLException occurrs. The issue is that my JPA datasource is unable to commit the changes to the Entity I do when the JDBC query fails.
It seems to me like the two datasources/transaction managers are not completely independent of each other and that an exception in the secondary datasource causes errors in the primary datasource during commiting changes.
Is this even possible? If yes, how can I configure two independent transaction managers?
EDIT:
I have already tried to annotate the respective methods and classes with #Transactional(noRollbackFor = Exception.class) but this does not solve the problem.
Here are the two Datasource configurations:
ApplicationDatasourceConfig (JPA)
#Configuration
#EnableJpaRepositories(basePackages = "foo.bar.repository.jpa",
entityManagerFactoryRef = "applicationEntityManagerFactory",
transactionManagerRef = "applicationTransactionManager")
#ConfigurationProperties(prefix = "spring.datasource.hikari")
#EnableTransactionManagement
public class ApplicationDatasourceConfig extends HikariConfig{
#Bean("applicationDatasource")
#Primary
public DataSource applicationDataSource(){
return new HikariDataSource(this);
}
#Bean("applicationDatasourceProperties")
#Primary
public DataSourceProperties dataSourceProperties(){
return new DataSourceProperties();
}
#Bean("applicationEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean applicationEntityManagerFactory(EntityManagerFactoryBuilder builder,
#Qualifier("applicationDatasource") DataSource dataSource){
return builder
.dataSource(dataSource)
.packages("foo.bar.entity")
.build();
}
#Bean("applicationTransactionManager")
#Primary
public PlatformTransactionManager applicationTransactionManager(#Qualifier("applicationEntityManagerFactory")EntityManagerFactory entityManagerFactory){
return new JpaTransactionManager(entityManagerFactory);
}
}
SecondaryDatasourceConfig (JDBC)
#Configuration
#ConfigurationProperties(prefix = "secondary.datasource")
#EnableTransactionManagement
public class SecondaryDatasourceConfig {
#Bean("secondaryDatasource")
public DataSource secondaryDataSource(){
return secondaryDataSourceProperties().initializeDataSourceBuilder().build();
}
#Bean
public DataSourceProperties secondaryDataSourceProperties(){
return new DataSourceProperties();
}
#Bean("secondaryTransactionManager")
public PlatformTransactionManager secondaryTransactionManager(#Qualifier("secondaryDatasource") DataSource secondaryDataSource){
return new DataSourceTransactionManager(secondaryDataSource);
}
}
I would like to know if Spring offers any support for Auto Fail over of Data Sources? For example, if the primary data source is down, connect to secondary. Any suggestions to effectively configure auto fail over of Data sources is greatly appreciated.
Thanks.
The Primary/DR setup for DB should be managed at DB level. Its not right to switch that in code. But to answer your question "can connect to 2 Data sources in Spring". Yes you can easier if you are using Spring-boot.
Few details from spring documentation.
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 have tried every option on web but not able to set the values in following method:
#Configuration
#PropertySource("classpath:application.properties")
public class MyDataSource {
#Value("${db.driver}")
private String DB_DRIVER;
#Value("${db.url}")
private String DB_URL;
#Value("${db.username}")
private String DB_USERNAME;
#Value("${db.password}")
private String DB_PASSWORD;
#Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean
public DataSource getDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(DB_DRIVER);
dataSource.setUrl(DB_URL);
dataSource.setUsername(DB_USERNAME);
dataSource.setPassword(DB_PASSWORD);
return dataSource;
}
}
My application.properties is in main/resources folder and values can be seen in variables in debug mode. But on running app, it shows Property ' ' must not be empty.
EDIT: I am not sure what can be the issue in first case?
So changed the application.property file as suggested and code as below :
#Autowired
protected JdbcTemplate jdbcTemp;
public List<> getData(String id) {
return jdbcTemp.query("SELECT ........,new RowMapper());
}
But getting java.lang.NullPointerException:
If you're using Spring Boot, you can leverage application.properties file by declaring some entries:
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass
In this way there is no need to implement a #Configuration class to setup database connection in Spring Boot.
You can deepen more here:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-sql.html
By the way, take a look at spring.io
For the java configuration, using Environment instance to obtain the properties seems to be the preferred way, as by default ${..} placeholders are not resolved.
You may use something like this:
#Autowired
private Environment env;
#Bean
public DataSource getDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("db.driver");
.....
return dataSource;
}
Reasons from the Spring Jira:
it's inconsistent. #PropertySource is the declarative counterpart to ConfigurableEnvironment#addPropertySource. We do not add a
PropertySourcesPlaceholderConfigurer in the latter case, and it would
be inconsistent to do so in the former. it will not be what the user
intended in every (or even most) cases.
It is entirely possible, and even recommended that #Configuration class users forego $ {...} property replacement entirely, in favor of
Environment#getProperty lookups within #Bean methods. For users
following this recommendation, the automatic registration of a
PropertySorucesPlaceholderConfigurer would be confusing when noticed,
and generally undesirable as it's one more moving part. Yes, it's
presence is benign, but not cost-free. a PSPC must visit every bean
definition in the container to interrogate PropertyValues, only to do
nothing in cases where users are going with the
Environment#getProperty approach.
it is solvable (and already solved) by documentation. Proper use of #PropertySource, PropertySourcesPlaceholderConfigurer and other
components is pretty comprehensively documented in the Javadoc for
#Configuration already, and reference documentation is soon to follow.
Me too was getting the error when tried to switch from MySQL to MSSQL. The actual issue was I forgot to put the MSSQL dependency in the service. I used mssql-jdbc
I've been successful using using the Accessing Data With JPA tutorial for Spring. I've gotten a CrudRepository of my own to work automatically by just configuring a specific DataSource #Bean, and the internal connections between these are managed by Spring Data (or Spring Boot, it's hard to tell which).
However, I can't figure out how to get that automated plumbing to handle a second DataSource #Bean. Injecting a second one causes the autoconfiguration classes to explode during startup.
Any thoughts as to how to do this? The searches I've done for this resulted in articles discussing multiple homogeneous DataSources for load balancing or other purposes, which is really not what I need. I have multiple databases with completely separate content that I need to pull into this app and I'd really like to avoid having to replicate all that automated configuration just because a second database entered the mix.
I'm hoping this is simple, but I'm fearful that it's an unsupported edge case in the autoconfiguration.
You can create two datasources and entitymanagers, one bean of them mark as #Primary
#Configuration
#EnableJpaRepositories(basePackages = "io.eddumelendez.springdatajpa.repository1")
public class FirstConfiguration {
#ConfigurationProperties(prefix = "datasource.postgres")
#Bean
#Primary
public DataSource postgresDataSource() {
return DataSourceBuilder.create().
build();
}
#Bean(name = "entityManagerFactory")
#Primary
public LocalContainerEntityManagerFactoryBean emf1(EntityManagerFactoryBuilder builder){
return builder
.dataSource(postgresDataSource())
.packages("io.eddumelendez.springdatajpa.domain1")
.persistenceUnit("users")
.build();
}
}
Configuration for another datasource:
#Configuration
#EnableJpaRepositories(basePackages = "io.eddumelendez.springdatajpa.repository2", entityManagerFactoryRef = "emf2")
public class SecondConfiguration {
#Bean
#ConfigurationProperties(prefix = "datasource.mysql")
public DataSource mysqlDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
public LocalContainerEntityManagerFactoryBean emf2(EntityManagerFactoryBuilder builder){
return builder
.dataSource(mysqlDataSource())
.packages("io.eddumelendez.springdatajpa.domain2")
.persistenceUnit("customers")
.build();
}
}
Your application.properties should looks like this:
datasource.mysql.url=jdbc:mysql://localhost:3306/mysql_demo
datasource.mysql.username=root
datasource.mysql.password=root
datasource.postgres.url=jdbc:postgresql://localhost:5432/postgres_demo
datasource.postgres.username=postgres
datasource.postgres.password=postgres