spring boot swing application with multiple data sources - java

I am trying to build a standalone desktop spring application, the application should get the information from multiple data sources. The problem I facing is that #Autowired Repository is null Bellow is my project setup, I thinking because of the application is desktop I am missing some configuration
Main Class
#SpringBootApplication(exclude = HibernateJpaAutoConfiguration.class)
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class)
.headless(false)
.web(false)
.run(args);
}
#Bean
public Main frame() {
return new Main();
}
}
DB Config Second is configured the same way. Additionally here IntelliJ shows that EntityManagerFactoryBuilder builder bean cannot be autowired
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "notificationEntityManagerFactory",
basePackages = { "org.thebot.notification.repository" })
public class NotificationDBConfig {
#Primary
#Bean(name = "notificationDataSource")
#ConfigurationProperties(prefix="spring.notificationDatasource")
public DataSource notificationDataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = "notificationEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean notificationEntityManagerFactory(
EntityManagerFactoryBuilder builder,
#Qualifier("notificationDataSource") DataSource dataSource) {
return builder
.dataSource(dataSource)
.packages("org.thebot.notification.domain")
.persistenceUnit("notification")
.build();
}
#Primary
#Bean(name = "notificationTransactionManager")
public PlatformTransactionManager notificationTransactionManager(
#Qualifier("notificationEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
}
Repository
#Repository
public interface NotificationRepository extends JpaRepository<Notification, Long> {
}
Place where the repository is needed
public class NotificationTab {
#Autowired
private PopulationRepository populationRepository;
private JPanel notificationPanel;
}

Related

Dynamic Bean configuration & loading in spring boot

I am following this link for understanding hexagonal architecture with spring boot. The infrastructure section contains the configuration for the service bean and the repository is passed as a parameter as a below method.
Configuration
#Configuration
#ComponentScan(basePackageClasses = HexagonalApplication.class)
public class BeanConfiguration {
#Bean
BankAccountService bankAccountService(BankAccountRepository repository) {
return new BankAccountService(repository, repository);
}
}
I am not using JPA instead using Spring JDBC for interacting to DB. Linked tutorial is using JPA.
Lets say I have different database implementations i.e.. postgresql(BankAccountRepository) and db2(BankAccountDB2Rep) . I want to change the beans without touching the code. something like with yml configuration or something which I can maintain separately instead of touching the code.
BankAccountRepository.java
#Component
public class BankAccountRepository implements LoadAccountPort, SaveAccountPort {
private SpringDataBankAccountRepository repository;
// Constructor
#Override
public Optional<BankAccount> load(Long id) {
return repository.findById(id);
}
#Override
public void save(BankAccount bankAccount) {
repository.save(bankAccount);
}
}
How can I achieve the same in spring boot? Any help is appreciated..
You can refer to
Spring Boot Configure and Use Two DataSources for creating multiple datasources and do something like following.
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactory",
transactionManagerRef = "transactionManager",
basePackages = {
"com.example"
}
)
public class JPAConfig {
#Primary
#Bean(name = "postgresDataSource")
#ConfigurationProperties(prefix = "postgres.datasource")
public DataSource postgresDataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = "db2DataSource")
#ConfigurationProperties(prefix = "db2.datasource")
public DataSource db2DataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder builder,
#Qualifier("postgresDataSource") DataSource postgresdataSource,
#Qualifier("db2DataSource") DataSource db2dataSource,
#Value("${useDb2}") Boolean useDb2
) {
return builder
.dataSource(useDb2? db2dataSource : postgresdataSource)
.packages("com.example")
.persistenceUnit("db1")
.build();
}
#Primary
#Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(
#Qualifier("entityManagerFactory") EntityManagerFactory entityManagerFactory
) {
return new JpaTransactionManager(entityManagerFactory);
}
}
As mentioned by #M.Deinum in comments, the issue can be resolved by using the spring conditional beans, as below
#Configuration
#ConditionalOnProperty(
value="module.enabled",
havingValue = "true",
matchIfMissing = true)
class CrossCuttingConcernModule {
...
}
More information can be found here

Spring Boot auto generate tables from wrong datasource

My current project needs to connect to multiple databases. I set
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=update
in application.properties.
and I have some dbConfig as below:
#Configuration
public class DBSourceConfiguration {
public final static String DATA_SOURCE_PRIMARY = "dataSource";
public final static String DATA_SOURCE_PROPERTIES = "propertiesDataSource";
public final static String DATA_SOURCE_REPORT = "reportDataSource";
public final static String DATA_SOURCE_NEW_DRAGON = "newDragonDataSource";
#Primary
#Bean(name = DATA_SOURCE_PRIMARY)
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = DATA_SOURCE_REPORT)
#ConfigurationProperties(prefix = "externaldatasource.report")
public DataSource reportDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = DATA_SOURCE_NEW_DRAGON)
#ConfigurationProperties(prefix = "externaldatasource.newdragon")
public DataSource newDragonDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = DATA_SOURCE_PROPERTIES)
#ConfigurationProperties(prefix = "externaldatasource.properties")
public DataSource propertiesDataSource() {
return DataSourceBuilder.create().build();
}
}
and
<!-- language: Java -->
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = PrimaryDbConfig.ENTITY_MANAGER_FACTORY,
transactionManagerRef = PrimaryDbConfig.TRANSACTION_MANAGER,
basePackageClasses = { _TbsRepositoryBasePackage.class })
public class PrimaryDbConfig extends AbstractDbConfig {
public final static String ENTITY_MANAGER_FACTORY = "entityManagerFactoryPrimary";
public final static String ENTITY_MANAGER = "entityManagerPrimary";
public final static String TRANSACTION_MANAGER = "transactionManagerPrimary";
#Autowired
#Qualifier(DBSourceConfiguration.DATA_SOURCE_PRIMARY)
private DataSource dataSource;
#Primary
#Bean(name = ENTITY_MANAGER_FACTORY)
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder) {
return builder.dataSource(dataSource).properties(getVendorProperties(dataSource)).packages(_TbsEntityBasePackage.class).persistenceUnit("primaryPersistenceUnit").build();
}
#Primary
#Bean(name = ENTITY_MANAGER)
public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
return entityManagerFactory(builder).getObject().createEntityManager();
}
#Primary
#Bean(name = TRANSACTION_MANAGER)
public PlatformTransactionManager transactionManager(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(entityManagerFactory(builder).getObject());
}
}
and
<!-- language: Java -->
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = PropertiesDbConfig.ENTITY_MANAGER_FACTORY,
transactionManagerRef = PropertiesDbConfig.TRANSACTION_MANAGER,
basePackageClasses = { _PropertiesRepositoryBasePackage.class })
public class PropertiesDbConfig extends AbstractDbConfig {
public final static String ENTITY_MANAGER_FACTORY = "entityManagerFactoryProperties";
public final static String ENTITY_MANAGER = "entityManagerProperties";
public final static String TRANSACTION_MANAGER = "transactionManagerProperties";
#Autowired
#Qualifier(DBSourceConfiguration.DATA_SOURCE_PROPERTIES)
private DataSource dataSource;
#Bean(name = ENTITY_MANAGER_FACTORY)
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder) {
return builder.dataSource(dataSource).properties(getVendorProperties(dataSource)).packages(_PropertiesEntityBasePackage.class).persistenceUnit("propertiesPersistenceUnit").build();
}
#Bean(name = ENTITY_MANAGER)
public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
return entityManagerFactory(builder).getObject().createEntityManager();
}
#Bean(name = TRANSACTION_MANAGER)
public PlatformTransactionManager transactionManager(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(entityManagerFactory(builder).getObject());
}
}
and two more DBConfig classes(just like two DbConfig classes above).
My problem is every time I run this web application, Entities (under different packages) will generate to all databases. In other words, Tbs's(Primary) entities will generate tables to newDragon and all other databases.
For instance, Entity A belongs to primary data source, Entity B belongs to properties datasouce. But framework generates table A, B to both primary database and newDragon database and other two database.
Update 2018/06/01 - 1
Although framework generate all entities to all databases, but I can still access tables from the right database. All my web application functionalities work very well. This is very odd, isn't it?
I guess my configuration is fine, so that there is no any problems while my application access database (like read from wrong database and get empty result or insert data to wrong database, etc). Probably something else cause this gernerte all tables to all databases problem.
Based on the configuration you provided, CRUD tables from the right database shouldn't be problem. But generating tables into the right database, sometimes you may want to check whether the configuration picks entity/package names correctly or not.
Each LocalContainerEntityManagerFactoryBean is set with unique package class, the framework will then scan entities under this package name and generate tables accordingly at target datasource; however, there's a situation the packageToScan will be changed. As you have #EntityScan annotation, it would
overrides packagesToScan on all defined LocalContainerEntityManagerFactoryBean, reference code as follow: EntityScanRegistrar.java
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof LocalContainerEntityManagerFactoryBean) {
LocalContainerEntityManagerFactoryBean factoryBean = (LocalContainerEntityManagerFactoryBean) bean;
factoryBean.setPackagesToScan(this.packagesToScan);
this.processed = true;
}
return bean;
}
As a result, even you have provided each LocalContainerEntityManagerFactoryBean with unique package class, the final result may still be overriden by the framework if you've #EntityScan somewhere at your application. Your configuration seems ok to me, so try to find and resolve the package names between #EntityScan and LocalContainerEntityManagerFactoryBean first, it should solve the issue.
reference: https://github.com/spring-projects/spring-boot/issues/6830

Prevent Spring container from performing injection on provided Bean

My goal is to use Spring Batch with different instances of DataSource for my ItemWriter and the JobRepository respectively which should work like this.
Unfortunately the Spring container injects the primary datasource at a later stage which I can confirm via debugger. Here's my configuration:
#RunWith(SpringJUnit4ClassRunner.class)
#DataJpaTest
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
#SpringBootTest(classes = { BatchTest.DatabaseConfig.class, BatchTest.BatchTestConfig.class })
public class BatchTest {
#Configuration
static class DatabaseConfig {
#Bean
#Primary
#ConfigurationProperties("spring.datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create()
.build();
}
#Bean
#ConfigurationProperties("spring.secondaryDatasource")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create()
.build();
}
}
#Configuration
#EnableBatchProcessing
static class BatchTestConfig {
#Bean()
BatchConfigurer configurer(#Qualifier("secondaryDataSource") DataSource dataSource) {
return new DefaultBatchConfigurer(dataSource);
}
}
}
I reckon this is due to the setter-injection defined in
package org.springframework.batch.core.configuration.annotation;
#Component
public class DefaultBatchConfigurer implements BatchConfigurer {
#Autowired(required = false)
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
this.transactionManager = new DataSourceTransactionManager(dataSource);
}
}
So now I'm wondering how above mentioned SO response works or rather doesn't work in my case. Can I somehow disable the additional setter-injection on the provided bean?
Ttry to override DefaultBatchConfigurer#setDataSource and add the qualifier to the setDataSource method:
#Bean()
BatchConfigurer configurer(#Qualifier("secondaryDataSource") DataSource dataSource) {
return new DefaultBatchConfigurer(dataSource) {
#Autowired(required = false)
public void setDataSource(#Qualifier("secondaryDataSource") DataSource dataSource) {
super.setDataSource(dataSource);
}
};
}
I agree it's a bit odd, but it's odd too that spring batch has such a constraint.
You could even try to override without any annotation at all. I don't remember if Spring searches annotation too in the class hiearchy.

Spring boot - how to configure multiple datasources

I am trying to setup multiple data sources(MySql, Postgres & Oracle) using Spring boot. I am not using JPA. Setting up with a JdbcTemplate.
I have tried setting up something like this.
application.properties
spring.datasource.test-oracle.username=test-oracle
spring.datasource.test-oracle.password=test-password
spring.datasource.test-oracle.url=dburl/test
spring.datasource.test-oracle.driver-class-name=oracle.jdbc.OracleDriver
spring.datasource.int-oracle.username=int-oracle
spring.datasource.int-oracle.password=int-password
spring.datasource.int-oracle.url=dburl/int
spring.datasource.int-oracle.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.d.int-mysql.username=user
spring.datasource.d.int-mysql.password=password
spring.datasource.d.int-mysql.url=dburl/d
spring.datasource.d.int-mysql.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.m.int-mysql.username=user
spring.datasource.m.int-mysql.password=password
spring.datasource.m.int-mysql.url=dburl/m
spring.datasource.m.int-mysql.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.d.test-mysql.username=user
spring.datasource.d.test-mysql.password=password
spring.datasource.d.test-mysql.url=dburl/d
spring.datasource.d.test-mysql.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.m.test-mysql.username=user
spring.datasource.m.test-mysql.password=password
spring.datasource.m.test-mysql.url=dburl/m
spring.datasource.m.test-mysql.driver-class-name=com.mysql.jdbc.Driver
MySqlConfiguration.java
#Configuration
public class MySqlConfiguration() {
#Bean(name = "dMySql")
#ConfigurationProperties(prefix = "spring.datasource.d.int-mysql")
public DataSource mysqlDrupalDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "dJdbc")
public JdbcTemplate drupalJdbcTemplate(DataSource dMySql) {
return new JdbcTemplate(dMySql);
}
#Bean(name = "mMySql")
#ConfigurationProperties(prefix = "spring.datasource.m.int-mysql")
public DataSource mysqlDrupalDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "mJdbc")
public JdbcTemplate drupalJdbcTemplate(DataSource mMySql) {
return new JdbcTemplate(mMySql);
}
}
OracleConfiguration.java
#Configuration
public class OracleConfiguration {
#Primary
#Bean(name = "tOracle")
#ConfigurationProperties(prefix = "spring.datasource.test-oracle")
public DataSource heOracleDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "tOracleJdbc")
public JdbcTemplate jdbcTemplate(DataSource tOracle) {
return new JdbcTemplate(tOracle);
}
#Bean(name = "iOracle")
#ConfigurationProperties(prefix = "spring.datasource.int-oracle")
public DataSource heOracleDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "iOracleJdbc")
public JdbcTemplate jdbcTemplate(DataSource iOracle) {
return new JdbcTemplate(iOracle);
}
}
I am not sure if the above is the correct way to go about this. When I use #Primary as per the boot docs, the Bean that has #Primary is always used. Then I use the configurations in my DAO implementations like this
One of the DAO Implementation
#Repository
public class DAOImpl implements DAOInterface {
#Autowired
#Qualifier("dJdbc")
private JdbcTemplate jdbc;
#Override
public Map<String, Object> getBasicStudentInfo(String MAIL) {
return jdbc.queryForMap(GET_BASIC_STUDENT_INFO, new Object[]{MAIL});
}
How do I go about doing this.? I did see many articles which is about mutliple datasources but unfortunately the examples or solutions don't suite me.
Further to this I need to be able to query against the DB's based on some user input. So if a user provides an environment e.g., "test" or "int", how can I trigger the correct properties based on that input.
I understand that Environment is #Autowired into Spring boot and I can intercept the user input, but unsure how I should provide the plumbing between the user input and the DAO configurations.
If something is unclear or needs a bit more explanation from my side or need more code I can provide that. Any help to resolve this situation would be appreciated.Thanks
Here is complete solution to your problem ...
Your configuration classes will look like this :
MySqlConfiguration.java
#Configuration
public class MySqlConfiguration {
#Bean(name = "dMySql")
#ConfigurationProperties(prefix = "spring.datasource.d.int-mysql")
public DataSource mysqlDrupalDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "dJdbc")
public JdbcTemplate drupalJdbcTemplate(#Qualifier("dMySql") DataSource dMySql) {
return new JdbcTemplate(dMySql);
}
#Bean(name = "mMySql")
#ConfigurationProperties(prefix = "spring.datasource.m.int-mysql")
public DataSource mysqlDrupalDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "mJdbc")
public JdbcTemplate drupalJdbcTemplate(#Qualifier("mMySql") DataSource mMySql) {
return new JdbcTemplate(mMySql);
}
}
OracleConfiguration.java
#Configuration
public class OracleConfiguration {
#Primary
#Bean(name = "tOracle")
#ConfigurationProperties(prefix = "spring.datasource.test-oracle")
public DataSource heOracleDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "tOracleJdbc")
public JdbcTemplate jdbcTemplate(#Qualifier("tOracle") DataSource tOracle) {
return new JdbcTemplate(tOracle);
}
#Bean(name = "iOracle")
#ConfigurationProperties(prefix = "spring.datasource.int-oracle")
public DataSource heOracleDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "iOracleJdbc")
public JdbcTemplate jdbcTemplate(#Qualifier("iOracle") DataSource iOracle) {
return new JdbcTemplate(iOracle);
}
}
and in your DAO class , you can autowire the JdbcTemplate like this :
#Repository
public class DAOImpl implements DAOInterface {
#Autowired
#Qualifier("dJdbc")
private JdbcTemplate dJdbc;
#Autowired
#Qualifier("mJdbc")
private JdbcTemplate mJdbc;
#Autowired
#Qualifier("tOracleJdbc")
private JdbcTemplate tOracleJdbc;
#Autowired
#Qualifier("iOracleJdbc")
private JdbcTemplate iOracleJdbc;
#Override
public Map<String, Object> getBasicStudentInfo(String MAIL) {
return dJdbc.queryForMap(GET_BASIC_STUDENT_INFO, new Object[]{MAIL});
}
.
.
.
}
Note: Make Sure to annotate one of DataSource with #Primary annotation
My setup: spring-boot version 1.2.5.RELEASE
I succeeded in running a setup like this, with the jdbc being created with the correct DataSources by adding a #Qualifier in each JDBC method creation
So, for every JDBC method you should match the qualifying datasource like this
#Bean(name = "dJdbc")
public JdbcTemplate drupalJdbcTemplate(#Qualifier("dMySql") DataSource dMySql) {
return new JdbcTemplate(dMySql);
}
No matter you choose for #Primary, using the #Qualifier for every JDBC should work good.
Autowiring jdbcTemplates in repositories, and using #Qualifier for them is ok also.
In your DAO you could wire in additional jdbctemplates. Then at runtime you can pick which one to use.
#Repository
public class DAOImpl implements DAOInterface {
#Autowired
#Qualifier("tOracle")
private JdbcTemplate testJdbc;
#Autowired
#Qualifier("intOracle")
private JdbcTemplate intJdbc;
#Override
public Map<String, Object> getBasicStudentInfo(String MAIL, String source) {
if ("TEST".equals(source)){
return testJdbc.queryForMap(GET_BASIC_STUDENT_INFO, new Object[]{MAIL});
}else {
return intJdbc.queryForMap(GET_BASIC_STUDENT_INFO, new Object[]{MAIL});
}
}

Two data sources in a Spring Boot application

I have a Spring Boot app for which I have configured two data sources. So far I've configured the data sources in my Application class (annotated with #EnableAutoConfiguration):
#Bean
#Primary
#ConfigurationProperties(prefix="datasource.db1")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean
#ConfigurationProperties(prefix="datasource.db2")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
I also added the configuration values to application.properties:
datasource.db1.url=...
...
datasource.db2.url=...
...
Since db1 is the #Primary data source, it is chosen by default. How do I tell an interface extending JpaRepository that it should use db2 instead?
UPDATE: mentioning that my repository is an interface.
You can get the bean associated to the secondary data source from the application context.
For example in Application.java (I'm also using Spring Boot) you define:
#Bean
#ConfigurationProperties(prefix="datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
and in a service (here for calling a stored procedure) you have:
#Service
public class EngineImpl implements EngineDao {
private SetScartiProcedure setScarti;
#Autowired
public void init(ApplicationContext ctx) {
DataSource dataSource = (DataSource) ctx.getBean("secondaryDataSource");
this.setScarti = new SetScartiProcedure(dataSource);
}
public class SetScartiProcedure extends StoredProcedure {
...
}
based on this you can define several DataSourcethis way
#Bean
public LocalContainerEntityManagerFactoryBean customerEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
return builder
.dataSource(customerDataSource())
.packages(Customer.class)
.persistenceUnit("customers")
.build();
}
#Bean
public LocalContainerEntityManagerFactoryBean orderEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
return builder
.dataSource(orderDataSource())
.packages(Order.class)
.persistenceUnit("orders")
.build();
}
and then bind each one of them to different classes that each one of them manages
#Configuration
#EnableJpaRepositories(basePackageClasses = Customer.class,
entityManagerFactoryRef = "customerEntityManagerFactory")
public class CustomerConfiguration {
...
}
#Configuration
#EnableJpaRepositories(basePackageClasses = Order.class,
entityManagerFactoryRef = "orderEntityManagerFactory")
public class OrderConfiguration {
...
}
the repositories should know which database to use by the DataSource that was bidden to the class

Categories