Spring boot Read write Split / Master - Slave / Multiple Databases - spring-boot

I am following this link
https://github.com/kwon37xi/replication-datasource
I have implemented the code But STILL Both my service functions are using the same Database(one which is marked primary)
Service Class
public class TableService{
#Autowired
private Table1Repo t1Repo;
#Transactional(readOnly = false)
public void saveTable1(Table1 t,int a, Table1 t2){
try{
t1Repo.save(t2);
}
catch(Exception e){
System.out.println("Inside");
}
}
#Transactional(readOnly = true)
public Table1 getTable(int id){
return t1Repo.findOne(id);
}
}
Then Added two Class(from the link)
ReplicationRoutingDataSource
public class ReplicationRoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
String dataSourceType = TransactionSynchronizationManager.isCurrentTransactionReadOnly() ? "read" : "write";
return dataSourceType;
}
}
WithRoutingDataSourceConfig
#Configuration
public class WithRoutingDataSourceConfig {
/*#Bean(destroyMethod = "shutdown")*/
#Bean
#Primary
#ConfigurationProperties(prefix="datasource.primary")
public DataSource writeDataSource() {
/* EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder()
.setName("routingWriteDb")
.setType(EmbeddedDatabaseType.H2)
.setScriptEncoding("UTF-8")
.addScript("classpath:/writedb.sql");
return builder.build();*/
return DataSourceBuilder.create().build();
}
/* #Bean(destroyMethod = "shutdown")*/
#Bean
#ConfigurationProperties(prefix="datasource.secondary")
public DataSource readDataSource() {
/*EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder()
.setName("routingReadDb")
.setType(EmbeddedDatabaseType.H2)
.setScriptEncoding("UTF-8")
.addScript("classpath:/readdb.sql");
return builder.build();*/
return DataSourceBuilder.create().build();
}
/**
* {#link org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource}는
* {#link org.springframework.beans.factory.InitializingBean}을 구현하므로,
* 명시적으로 afterPropertiesSet()메소드를 호출하거나
* 별도 #Bean으로 만들어 Spring Life Cycle을 타도록 해야 한다.
*/
#Bean
public DataSource routingDataSource(#Qualifier("writeDataSource") DataSource writeDataSource, #Qualifier("readDataSource") DataSource readDataSource) {
ReplicationRoutingDataSource routingDataSource = new ReplicationRoutingDataSource();
Map<Object, Object> dataSourceMap = new HashMap<Object, Object>();
dataSourceMap.put("write", writeDataSource);
dataSourceMap.put("read", readDataSource);
routingDataSource.setTargetDataSources(dataSourceMap);
routingDataSource.setDefaultTargetDataSource(writeDataSource);
return routingDataSource;
}
/**
* {#link org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy}로 감싸서
* 트랜잭션 동기화가 이루어진 뒤에 실제 커넥션을 확보하도록 해준다.
*
* #param routingDataSource
* #return
*/
#Bean
public DataSource dataSource(#Qualifier("routingDataSource") DataSource routingDataSource) {
return new LazyConnectionDataSourceProxy(routingDataSource);
}
}
application.prop file
server.port=8089
spring.jpa.show-sql = true
spring.jpa.properties.hibernate.show_sql=true
# Primary DataSource configuration
datasource.primary.url=jdbc:mysql://127.0.0.1:3306/jpa
datasource.primary.username=root
datasource.primary.password=root
# Any of the other Spring supported properties below...
# Secondary DataSource configuration
datasource.secondary.url=jdbc:mysql://127.0.0.1:3306/jpa2
datasource.secondary.username=root
datasource.secondary.password=root
Repository
public interface Table1Repo extends JpaRepository<Table1, Integer>{}
Issue is my both service functions are using the primary Database. What am I missing. I only have these class. Rest I have one Controller
Edited
I have made by code work by adding this class
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages="com.example")
public class ReplicationDataSourceApplicationConfig {
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(#Qualifier("dataSource") DataSource dataSource) {
LocalContainerEntityManagerFactoryBean emfb = new LocalContainerEntityManagerFactoryBean();
emfb.setDataSource(dataSource);
emfb.setPackagesToScan("com.example");
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
emfb.setJpaVendorAdapter(jpaVendorAdapter);
return emfb;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslationPostProcessor() {
return new PersistenceExceptionTranslationPostProcessor();
}
}

I'm the writer of the link you refered.
Which data source do you use with Table1Repo?
You have to inject the specific bean "dataSource" to you JDBC call.
I guess #Primary "writeDataSource" is injected to your Repository.
Try to change #Primary to "dataSource" or find a way to inject "dataSource" to your repository.

you can also try in this way:
Spring Boot 2 with Multiple DataSource for Postgres Data Replication
and here is source code in github:
spring-boot-multi-data-source
The following link explans, how you can have multiple datasource
DB1 (write):
#Configuration
#ConfigurationProperties("spring.datasource-write")
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactoryWrite",
transactionManagerRef = "transactionManagerWrite",
basePackages = {"com.ehsaniara.multidatasource.repository.writeRepository"}
)
public class DataSourceConfigWrite extends HikariConfig {
public final static String PERSISTENCE_UNIT_NAME = "write";
#Bean
public HikariDataSource dataSourceWrite() {
return new HikariDataSource(this);
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryWrite(
final HikariDataSource dataSourceWrite) {
return new LocalContainerEntityManagerFactoryBean() {{
setDataSource(dataSourceWrite);
setPersistenceProviderClass(HibernatePersistenceProvider.class);
setPersistenceUnitName(PERSISTENCE_UNIT_NAME);
setPackagesToScan(MODEL_PACKAGE);
setJpaProperties(JPA_PROPERTIES);
}};
}
#Bean
public PlatformTransactionManager transactionManagerWrite(EntityManagerFactory entityManagerFactoryWrite) {
return new JpaTransactionManager(entityManagerFactoryWrite);
}
}
DB2 (read):
#Configuration
#ConfigurationProperties("spring.datasource-read")
#EnableTransactionManagement
#EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactoryRead",
transactionManagerRef = "transactionManagerRead",
basePackages = {"com.ehsaniara.multidatasource.repository.readRepository"}
)
public class DataSourceConfigRead extends HikariConfig {
public final static String PERSISTENCE_UNIT_NAME = "read";
#Bean
public HikariDataSource dataSourceRead() {
return new HikariDataSource(this);
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryRead(
final HikariDataSource dataSourceRead) {
return new LocalContainerEntityManagerFactoryBean() {{
setDataSource(dataSourceRead);
setPersistenceProviderClass(HibernatePersistenceProvider.class);
setPersistenceUnitName(PERSISTENCE_UNIT_NAME);
setPackagesToScan(MODEL_PACKAGE);
setJpaProperties(JPA_PROPERTIES);
}};
}
#Bean
public PlatformTransactionManager transactionManagerRead(EntityManagerFactory entityManagerFactoryRead) {
return new JpaTransactionManager(entityManagerFactoryRead);
}
}

Related

unable run Quartz JDBCJobStore with AbstractRoutingDataSource

I have implemented the application using Spring RoutingDataSource.
Spring -> DS1,
DS2
Based on the logged in URL I am changing the Data Source. it is working fine.
Coming to the quartz, I am unable to change the data source dynamically. Always jobs are getting scheduled on default data source.
#Configuration
public class SchedulerConfig {
#Autowired
private DataSource dataSource;
#Autowired
private QuartzProperties quartzProperties;
#Bean
public JobFactory jobFactory(ApplicationContext applicationContext) {
AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory();
jobFactory.setApplicationContext(applicationContext);
return jobFactory;
}
#Bean
public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory) {
Properties properties = new Properties();
properties.putAll(quartzProperties.getProperties());
SchedulerFactoryBean factory = new SchedulerFactoryBean();
factory.setJobFactory(jobFactory);
factory.setDataSource(dataSource);
factory.setGlobalJobListeners(jobListener());
factory.setQuartzProperties(properties);
return factory;
}
#Bean
public JobListenerSupport jobListener() {
return new JobListener();
}
}
Data Source Routing Configuration::
#Component
public class DataSourceRouting extends AbstractRoutingDataSource {
private DataSourceOneConfig dataSourceOneConfig;
private DataSourceTwoConfig dataSourceTwoConfig;
private DataSourceContextHolder dataSourceContextHolder;
public DataSourceRouting(DataSourceContextHolder dataSourceContextHolder, DataSourceOneConfig dataSourceOneConfig,
DataSourceTwoConfig dataSourceTwoConfig) {
this.dataSourceOneConfig = dataSourceOneConfig;
this.dataSourceTwoConfig = dataSourceTwoConfig;
this.dataSourceContextHolder = dataSourceContextHolder;
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put(DataSourceEnum.tenant1, dataSourceOneDataSource());
dataSourceMap.put(DataSourceEnum.tenant2, dataSourceTwoDataSource());
this.setTargetDataSources(dataSourceMap);
this.setDefaultTargetDataSource(dataSourceTwoDataSource());
}
#Override
protected Object determineCurrentLookupKey() {
return dataSourceContextHolder.getBranchContext();
}
public DataSource dataSourceOneDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl(dataSourceOneConfig.getUrl());
dataSource.setUsername(dataSourceOneConfig.getUsername());
dataSource.setPassword(dataSourceOneConfig.getPassword());
return dataSource;
}
public DataSource dataSourceTwoDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl(dataSourceTwoConfig.getUrl());
dataSource.setUsername(dataSourceTwoConfig.getUsername());
dataSource.setPassword(dataSourceTwoConfig.getPassword());
return dataSource;
}
}
Data Soruce Config ::
#RequiredArgsConstructor
#DependsOn("dataSourceRouting")
public class DataSourceConfig {
private final DataSourceRouting dataSourceRouting;
#Bean
#Primary
public DataSource dataSource() {
return dataSourceRouting;
}
#Bean(name = "entityManager")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(EntityManagerFactoryBuilder builder) {
return builder.dataSource(dataSource()).packages("com.model.entity").build();
}
#Bean(name = "transcationManager")
public JpaTransactionManager transactionManager(
#Autowired #Qualifier("entityManager") LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) {
return new JpaTransactionManager(entityManagerFactoryBean.getObject());
}
}

Data Migration from One Db to another using Spring batch and Spring boot

Im new to spring framework and I Want to migrate data from One DB to another using Spring boot n Batch
Im trying to read from an Mysql db with an item reader and write it to an Oracle db using an item writer of the same job.
Im able to read the data from the Mysql db but unable to write it to Oracle Db as the writer is trying it write it in Mysql Db itself.
Im not sure why the connection is not switching to Oracle db.
Please help me over here what im doing wrong.
Application properties
server.port=8082
spring.batch.job.enabled= false
spring.datasource.url = jdbc:mysql://localhost:3306/projectdb1
spring.datasource.username = root
spring.datasource.password = Mech_2015
spring.datasource.driverClassName = com.mysql.jdbc.Driver
#second db2 ...
db2.datasource.url = jdbc:oracle:thin:#localhost:1521:xe
db2.datasource.username = system
db2.datasource.password = Mech_2015
db2.datasource.driverClassName = oracle.jdbc.driver.OracleDriver
Entity Managers
package com.techprimers.springbatchexample1.entity.manger;
#Configuration
#EnableJpaRepositories(entityManagerFactoryRef = "radiusEntityManager", transactionManagerRef = "radiusTransactionManager", basePackages = "com.techprimers.springbatchexample1.repository.userrepo")
public class RadiusConfig {
private final PersistenceUnitManager persistenceUnitManager;
public RadiusConfig(ObjectProvider<PersistenceUnitManager> persistenceUnitManager) {
this.persistenceUnitManager = persistenceUnitManager.getIfAvailable();
}
#Bean
#ConfigurationProperties("spring.jpa")
public JpaProperties radiusJpaProperties() {
return new JpaProperties();
}
#Bean
#Primary
#ConfigurationProperties("spring.datasource")
public DataSourceProperties radiusDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#ConfigurationProperties(prefix = "spring.datasource.properties")
public DataSource radiusDataSource() {
return (DataSource) radiusDataSourceProperties().initializeDataSourceBuilder().type(DataSource.class).build();
}
#Bean(name = "radiusEntityManager")
public LocalContainerEntityManagerFactoryBean radiusEntityManager(JpaProperties radiusJpaProperties) {
EntityManagerFactoryBuilder builder = createEntityManagerFactoryBuilder(radiusJpaProperties);
return builder.dataSource(radiusDataSource()).packages(User.class).persistenceUnit("userDs").build();
}
#Bean
public JpaTransactionManager radiusTransactionManager(EntityManagerFactory radiusEntityManager) {
return new JpaTransactionManager(radiusEntityManager);
}
private EntityManagerFactoryBuilder createEntityManagerFactoryBuilder(JpaProperties radiusJpaProperties) {
JpaVendorAdapter jpaVendorAdapter = createJpaVendorAdapter(radiusJpaProperties);
return new EntityManagerFactoryBuilder(jpaVendorAdapter, radiusJpaProperties.getProperties(),
this.persistenceUnitManager);
}
private JpaVendorAdapter createJpaVendorAdapter(JpaProperties jpaProperties) {
AbstractJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setShowSql(jpaProperties.isShowSql());
adapter.setDatabase(jpaProperties.getDatabase());
adapter.setDatabasePlatform(jpaProperties.getDatabasePlatform());
adapter.setGenerateDdl(jpaProperties.isGenerateDdl());
return adapter;
}
}
package com.techprimers.springbatchexample1.entity.manger;
#Configuration
#EnableJpaRepositories(entityManagerFactoryRef = "esbEntityManager", transactionManagerRef = "esbDetailsTransactionManager", basePackages = "com.techprimers.springbatchexample1.repository.userdetails")
public class EsbConfig {
private final PersistenceUnitManager persistenceUnitManager;
public EsbConfig(ObjectProvider<PersistenceUnitManager> persistenceUnitManager) {
this.persistenceUnitManager = persistenceUnitManager.getIfAvailable();
}
#Bean
#ConfigurationProperties("db2.jpa")
public JpaProperties esbJpaProperties() {
return new JpaProperties();
}
#Bean
#ConfigurationProperties("db2.datasource")
public DataSourceProperties esbDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#ConfigurationProperties(prefix = "db2.datasource.properties")
public DataSource esbDataSource() {
return (DataSource) esbDataSourceProperties().initializeDataSourceBuilder().type(DataSource.class).build();
}
#Bean(name = "esbEntityManager")
public LocalContainerEntityManagerFactoryBean esbEntityManager(JpaProperties esbJpaProperties) {
EntityManagerFactoryBuilder builder = createEntityManagerFactoryBuilder(esbJpaProperties);
return builder.dataSource(esbDataSource()).packages(UserDetails.class).persistenceUnit("userDetailDs").build();
}
#Bean
public JpaTransactionManager esbTransactionManager(EntityManagerFactory esbEntityManager) {
return new JpaTransactionManager(esbEntityManager);
}
private EntityManagerFactoryBuilder createEntityManagerFactoryBuilder(JpaProperties esbJpaProperties) {
JpaVendorAdapter jpaVendorAdapter = createJpaVendorAdapter(esbJpaProperties);
return new EntityManagerFactoryBuilder(jpaVendorAdapter, esbJpaProperties.getProperties(),
this.persistenceUnitManager);
}
private JpaVendorAdapter createJpaVendorAdapter(JpaProperties jpaProperties) {
AbstractJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setShowSql(jpaProperties.isShowSql());
adapter.setDatabase(jpaProperties.getDatabase());
adapter.setDatabasePlatform(jpaProperties.getDatabasePlatform());
adapter.setGenerateDdl(jpaProperties.isGenerateDdl());
return adapter;
}
}
Batch Config
package com.techprimers.springbatchexample1.config;
#Configuration
#EnableBatchProcessing
public class SpringBatchConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(SpringBatchConfig.class);
#Bean
#Primary
#Qualifier("radiusDatasource")
#ConfigurationProperties(prefix = "spring.datasource")
DataSource mysqlDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
#ConfigurationProperties(prefix = "db2.datasource")
#Qualifier("esbDatasource")
DataSource oracleDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
public InspectionProcessor processor() {
return new InspectionProcessor();
}
#Bean
public JdbcCursorItemReader<User> reader() {
JdbcCursorItemReader<User> cursorItemReader = new JdbcCursorItemReader<>();
cursorItemReader.setDataSource(mysqlDataSource());
cursorItemReader.setSql("SELECT ID,CNAME,SID,CREATEDDATE,COMPLETEDDATE FROM INSPECTION");
cursorItemReader.setRowMapper(new InspectionDetailRowmapper());
return cursorItemReader;
}
private static final String QUERY_INSERT_STUDENT = "INSERT "
+ "INTO inspect(id,cname,completeddate,createddate,lastupdateddate,sid) " + "VALUES (?,?,?,?,?,?)";
#Bean
ItemWriter<UserDetails> databaseItemWriter(DataSource dataSource, NamedParameterJdbcTemplate jdbcTemplate) {
LOGGER.info("Starting writer");
JdbcBatchItemWriter<UserDetails> databaseItemWriter = new JdbcBatchItemWriter<>();
databaseItemWriter.setDataSource(oracleDataSource());
databaseItemWriter.setJdbcTemplate(jdbcTemplate);
LOGGER.info(" writer");
databaseItemWriter.setSql(QUERY_INSERT_STUDENT)
ItemPreparedStatementSetter<UserDetails> valueSetter = new UserDetailsPreparedStatementSetter();
databaseItemWriter.setItemPreparedStatementSetter(valueSetter);
return databaseItemWriter;
}
#Bean
Step dataMigrationStep(ItemReader<User> reader, ItemProcessor<User, UserDetails> processor,
ItemWriter<UserDetails> databsaeItemWriter, StepBuilderFactory stepBuilderFactory) {
return stepBuilderFactory.get("dataMigrationStep").<User, UserDetails>chunk(5).reader(reader)
.processor(processor).writer(databsaeItemWriter).build();
}
#Bean
Job dataMigrationJob(JobBuilderFactory jobBuilderFactory, #Qualifier("dataMigrationStep") Step dataMigrationStep) {
return jobBuilderFactory.get("csvFileToDatabaseJob").incrementer(new RunIdIncrementer()).flow(dataMigrationStep)
.end().build();
}
}
Your writer does not inject properly the database where to write your data
this
#Bean
ItemWriter<UserDetails> databaseItemWriter(DataSource dataSource, NamedParameterJdbcTemplate jdbcTemplate)
instead of
#Bean
ItemWriter<UserDetails> databaseItemWriter(#Qualifier("esbDatasource") DataSource dataSource, NamedParameterJdbcTemplate jdbcTemplate)

Multiple DataBase Configuration for Spring Data JPA 2.0.9 Release

I have two JPA Configurations Classes. I need to configure two databases (Oracle Connections). While I connect with the JDBC template it works perfectly by using #PersistenceContext. while using JPA Repo it took the default initially but before execution, it took second and execute the query. I am very much confused about this. Will anyone clear my doubt?
Spring Boot- 2.0.9
Spring-data-jpa - 2.0.9
Hibernate - 5.2.18
My First Class(Default) - JPAConfig.java ,
#ConditionalOnProperty(name = "embedded", havingValue = "true", matchIfMissing = true)
#Configuration
#RefreshScope
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory", transactionManagerRef = "transactionManager", basePackages = {
"kgfsl" })
public class JPAConfig {
#Bean
public Validator getValidator() {
return new LocalValidatorFactoryBean();
}
#Value("${db.url}")
String dbUrl = "jdbc:oracle:thin:#10.100.1.95:1539:HFSL12C";
#Value("${db.driverClassName}")
String dbDriverClassName = "oracle.jdbc.driver.OracleDriver";
#Value("${db.userName}")
String dbUserName = "TENYV2";
#Value("${db.password}")
String dbPassword = "TENYV2";
#Value("${db.databaseType}")
String dbDatabaseType = "Oracle";
#Value("${encryptionOn:false}")
private boolean encryptionOn;
#Bean
#Primary
#RefreshScope
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(dbDriverClassName);
dataSource.setSchema(dbUserName);
dataSource.setUrl(dbUrl);
dataSource.setUsername(dbUserName);
if (encryptionOn)
dataSource.setPassword(CryptoUtil.decrypt(dbPassword));
else
dataSource.setPassword(dbPassword);
return dataSource;
}
#Bean
#Primary
#RefreshScope
public Map<String, Object> jpaProperties() {
Map<String, Object> props = new HashMap<>();
props.put("hibernate.dialect", ApplicationStartUpFactory.getDialect(dbDatabaseType));
props.put("javax.persistence.validation.mode", "none");
props.put("hibernate.id.new_generator_mappings", "false");
props.put("hibernate.default_schema", dbUserName);
return props;
}
public static int batchSize() {
return Integer.valueOf(Dialect.DEFAULT_BATCH_SIZE);
}
#Primary
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(true);
hibernateJpaVendorAdapter.setGenerateDdl(false);
hibernateJpaVendorAdapter.setDatabase(ApplicationStartUpFactory.getJPAVendor(dbDatabaseType));
hibernateJpaVendorAdapter.setDatabasePlatform(dbUserName);
return hibernateJpaVendorAdapter;
}
#Primary
#Bean
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager(entityManagerFactory().getObject());
}
#Bean()
#Primary
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(this.dataSource());
lef.setJpaPropertyMap(this.jpaProperties());
lef.setJpaVendorAdapter(this.jpaVendorAdapter());
String entityPackages = "kgfsl.*.*";
lef.setPackagesToScan(entityPackages.split(","));
lef.setPersistenceUnitName("default"); // <- giving 'default' as name
return lef;
}
#Bean()
#Qualifier("jdbcTemplate")
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(this.dataSource());
}
#Bean(name = "namedParameterJdbcTemplate")
public NamedParameterJdbcTemplate namedParameterJdbcTemplate() {
return new NamedParameterJdbcTemplate(this.dataSource());
}
#SuppressWarnings("unused")
private DatabasePopulator createDatabasePopulator() {
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
databasePopulator.setContinueOnError(false);
/*
* if (new ClassPathResource("basic_table.sql").exists())
* databasePopulator.addScript(new ClassPathResource("basic_table.sql"));
*/
return databasePopulator;
}
#Bean
#Primary
public PhysicalNamingStrategy physical() {
return new PhysicalNamingStrategyStandardImpl();
}
#Bean
#Primary
public ImplicitNamingStrategy implicit() {
return new ImplicitNamingStrategyJpaCompliantImpl();
}
MY Second Class(Not Default) - JPADataSyncConfig.java,
#ConditionalOnProperty(name = "embedded", havingValue = "true", matchIfMissing = true)
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "dataSyncEntityManagerFactory", transactionManagerRef = "dataSyncTransactionManager", basePackages = {
"kgfsl" })
public class JPADataSyncConfig {
#Value("${db.syncurl}")
String dbUrl = "jdbc:oracle:thin:#10.100.1.94:1522:TENYY";
#Value("${db.syncurldriverClassName}")
String dbDriverClassName = "oracle.jdbc.driver.OracleDriver";
#Value("${db.syncuserName}")
String dbUserName = "MYNEWDB";
#Value("${db.syncpassword}")
String dbPassword = "MYNEWDB";
#Value("${db.syncurldatabaseType}")
String dbDatabaseType = "Oracle";
#Value("${encryptionOn:false}")
private boolean encryptionOn;
#Bean(name = "dataSyncDataSource")
public DataSource dataSyncDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(dbDriverClassName);
dataSource.setUrl(dbUrl);
dataSource.setUsername(dbUserName);
if (encryptionOn)
dataSource.setPassword(CryptoUtil.decrypt(dbPassword));
else
dataSource.setPassword(dbPassword);
return dataSource;
}
#Bean(name = "dataSyncJpaProperties")
public Map<String, Object> dataSyncJpaProperties() {
Map<String, Object> props = new HashMap<>();
props.put("hibernate.dialect", ApplicationStartUpFactory.getDialect(dbDatabaseType));
props.put("javax.persistence.validation.mode", "none");
props.put("hibernate.id.new_generator_mappings", "false");
return props;
}
public static int batchSize() {
return Integer.valueOf(Dialect.DEFAULT_BATCH_SIZE);
}
#Bean(name = "dataSyncJpaVendorAdapter")
public JpaVendorAdapter dataSyncJpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(false);
hibernateJpaVendorAdapter.setGenerateDdl(false);
hibernateJpaVendorAdapter.setDatabase(ApplicationStartUpFactory.getJPAVendor(dbDatabaseType));
return hibernateJpaVendorAdapter;
}
#Bean(name = "dataSyncTransactionManager")
public PlatformTransactionManager dataSyncTransactionManager() {
return new JpaTransactionManager(dataSyncEntityManagerFactory().getObject());
}
#Bean(name = "dataSyncEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean dataSyncEntityManagerFactory() {
LocalContainerEntityManagerFactoryBean lef = new LocalContainerEntityManagerFactoryBean();
lef.setDataSource(this.dataSyncDataSource());
lef.setJpaPropertyMap(this.dataSyncJpaProperties());
lef.setJpaVendorAdapter(this.dataSyncJpaVendorAdapter());
String entityPackages = "kgfsl.*.*";
lef.setPackagesToScan(entityPackages.split(","));
return lef;
}
#Bean(name = "dataSyncJdbcTemplate")
public JdbcTemplate dataSyncJdbcTemplate() {
return new JdbcTemplate(this.dataSyncDataSource());
}
}
My Method Call,
#Transactional(rollbackFor = Exception.class)
public List<Customer> listPending(Long formId) throws Exception, JsonParseException, JsonMappingException,
ClassNotFoundException, JSONException, IOException, ParseException, CustomException {
List<Customer> auditData = customerRepo.findByFormIdAndStatus(formId, 'P');
return auditData;
}
I solved my Problem by Changing the base path. It's Working Fine.
My Second Class,
#ConditionalOnProperty(name = "embedded", havingValue = "true", matchIfMissing = true)
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "dataSyncEntityManagerFactory", transactionManagerRef = "dataSyncTransactionManager", basePackages = {
"kgfsl.rms.datasync" })
public class JPADataSyncConfig {

Spring multiple datasource without primary

I have a problem, I need to create two DataSource but I cannot use #Primary because in other module I also have two DataSource and then in third module I include both modules, so there are two primary modules.
I wanted to use #Qualifier but it does not work.
#Bean(name = "secondDataSourceProperties")
#ConfigurationProperties("second")
public DataSourceProperties secondDataSourceProperties() {
return new DataSourceProperties();
}
#Bean(name = "secondDataSource")
#ConfigurationProperties("second.configuration")
public DataSource secondDataSource(#Qualifier("secondDataSourceProperties") DataSourceProperties dataSourceProperties) {
HikariDataSource ds = dataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
ds.setConnectionTestQuery("SELECT 1");
ds.setConnectionInitSql("SELECT 1");
ds.setPoolName("jdbc/second");
return ds;
}
#Bean(name = "secondTransactionManager")
public DataSourceTransactionManager secondDataSourceTransactionManager(#Qualifier("secondDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
#Bean
#ConfigurationProperties("first")
public DataSourceProperties firstDataSourceProperties() {
return new DataSourceProperties();
}
#Bean(name = "firstDataSource")
#ConfigurationProperties("first.configuration")
public DataSource firstDataSource(#Qualifier("firstDataSourceProperties") DataSourceProperties dataSourceProperties) {
HikariDataSource ds = dataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
ds.setConnectionTestQuery("SELECT 1");
ds.setConnectionInitSql("SELECT 1");
ds.setPoolName("jdbc/first");
return ds;
}
#Bean(name = "firstTransactionManager")
public DataSourceTransactionManager firstDataSourceTransactionManager(#Qualifier("firstDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
Error I'm getting is:
No qualifying bean of type
'org.springframework.boot.autoconfigure.jdbc.DataSourceProperties'
available: expected single matching bean but found 5:
secondDataSourceProperties,firstDataSourceProperties,3DataSourceProperties,4DataSourceProperties,spring.datasource-org.springframework.boot.autoconfigure.jdbc.DataSourceProperties
I use DataSource like that in code:
#Autowired
#Qualifier("first")
private DataSource dataSource;
and
#Transactional(value = "firstTransactionManager")
How about just doing the following? Without #Qualifier mess? The name of the method for #Bean is by default used as the "qualifier" of the bean.
#Bean
#ConfigurationProperties("first")
public DataSourceProperties firstDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
#ConfigurationProperties("second")
public DataSourceProperties secondDataSourceProperties() {
return new DataSourceProperties();
}
#Bean
public DataSource firstDataSource(DataSourceProperties firstDataSourceProperties) {
HikariDataSource ds = firstDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
ds.setConnectionTestQuery("SELECT 1");
ds.setConnectionInitSql("SELECT 1");
ds.setPoolName("jdbc/first");
return ds;
}
#Bean
public DataSource secondDataSource(DataSourceProperties secondDataSourceProperties) {
HikariDataSource ds = secondDataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
ds.setConnectionTestQuery("SELECT 1");
ds.setConnectionInitSql("SELECT 1");
ds.setPoolName("jdbc/second");
return ds;
}
#Bean
public DataSourceTransactionManager firstTransactionManager(DataSource firstDataSource) {
return new DataSourceTransactionManager(firstDataSource);
}
#Bean
public DataSourceTransactionManager secondTransactionManager(DataSource secondDataSource) {
return new DataSourceTransactionManager(secondDataSource);
}
You can just autowire with this name only;
#Autowire
private DataSource firstDateSource;
and
#Transactional("secondTransactionManager")
Helped adding
#EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
Or if you use #SpringBootApplication
#SpringBootApplication(scanBasePackages={"a.b.c"}, exclude = {DataSourceAutoConfiguration.class})

Hikaricp configuration for multiple datasources

I have a multi database application. Users can select the database on the login page.
Then the database is routing selected database thanks for AbstractRoutingDataSource from Spring.
I want to use HikariCP, but it needs dataSourceUrl. But my Datasource URL changes dynamically. How can I configure Hikaricp for multiple databases?
File application.properties:
#database1 properties
app.database1.connection.url = url1
app.database1.connection.username = sameusername
app.database1.connection.password = samepassword
#database2 properties
app.database2.connection.url = url2
app.database2.connection.username = sameusername
app.database2.connection.password = samepassword
My Datasource configuration class example:
public class DataSourceConfiguration {
#Autowired(required = false)
private PersistenceUnitManager persistenceUnitManager;
#Bean
#ConfigurationProperties(prefix = "app.database1.connection")
public DataSource database1DataSource() {
return DataSourceBuilder.create().build();
}
#Bean
#ConfigurationProperties(prefix = "app.database2.connection")
public DataSource database2DataSource() {
return DataSourceBuilder.create().build();
}
#Bean
#Primary
public DataSource appDataSource() {
DataSourceRouter router = new DataSourceRouter();
final HashMap<Object, Object> map = new HashMap<>(3);
map.put(DatabaseEnvironment.DATABASE1, database1DataSource());
map.put(DatabaseEnvironment.DATABASE2, database2DataSource());
router.setTargetDataSources(map);
return router;
}
#Bean
#Primary
#ConfigurationProperties("app.connection.jpa")
public JpaProperties appJpaProperties() {
return new JpaProperties();
}
private JpaVendorAdapter createJpaVendorAdapter(JpaProperties jpaProperties) {
AbstractJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setShowSql(jpaProperties.isShowSql());
adapter.setDatabase(jpaProperties.getDatabase());
adapter.setDatabasePlatform(jpaProperties.getDatabasePlatform());
adapter.setGenerateDdl(jpaProperties.isGenerateDdl());
return adapter;
}
My session scoped class instead of context holder:
#Component
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class PreferredDatabaseSession implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private DatabaseEnvironment preferredDb;
public DatabaseEnvironment getPreferredDb() {
return preferredDb;
}
public void setPreferredDb(DatabaseEnvironment preferredDb) {
this.preferredDb = preferredDb;
}
}
If I understand your requirement correctly, you intend to define two data sources and for a given request you want to route your queries to a particular data source based on some condition.
The solution is:
File application.properties
#database1 properties
app.database1.connection.url = url1
app.database1.connection.username = username1
app.database1.connection.password = password1
#database2 properties
app.database2.connection.url = url2
app.database2.connection.username = username2
app.database2.connection.password = password2
#default
default.datasource.key=dataSource1
File CommonRoutingDataSource.java
public class CommonRoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceName();
}
public void initDataSources(final DataSource dataSource1, final DataSource dataSource2,
final String defaultDataSourceKey) {
final Map<Object, Object> dataSourceMap = new HashMap<Object, Object>();
dataSourceMap.put("dataSource1", dataSource1);
dataSourceMap.put("dataSource2", dataSource2);
this.setDefaultTargetDataSource(dataSourceMap.get(defaultDataSourceKey));
this.setTargetDataSources(dataSourceMap);
}
}
File DataSourceContextHolder.java
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
private DataSourceContextHolder() {
// Private no-op constructor
}
public static final void setDataSourceName(final String dataSourceName) {
Assert.notNull(dataSourceName, "dataSourceName cannot be null");
contextHolder.set(dataSourceName);
}
public static final String getDataSourceName() {
return contextHolder.get();
}
public static final void clearDataSourceName() {
contextHolder.remove();
}
}
File DataSourceConfig.java
public class DataSourceConfig {
#Autowired
private Environment env;
#Autowired
#Bean(name = "dataSource")
public DataSource getDataSource(final DataSource dataSource1, final DataSource dataSource2) {
final CommonRoutingDataSource dataSource = new CommonRoutingDataSource();
dataSource.initDataSources(dataSource1, dataSource2, env.getProperty("default.datasource.key"));
return dataSource;
}
#Bean(name = "dataSource1")
public DataSource getDataSource1() throws SQLException {
// The exact DataSource class imported shall be as per your requirement - HikariCP, or Tomcat etc.
final DataSource dataSource = new DataSource();
dataSource.setDriverClassName();
dataSource.setUrl(env.getProperty("app.database1.connection.url"));
// Set all data source attributes from the application.properties file
return dataSource;
}
#Bean(name = "dataSource2")
public DataSource getDataSource2() throws SQLException {
// The exact DataSource class imported shall be as per your requirement - HikariCP, or Tomcat etc.
final DataSource dataSource = new DataSource();
dataSource.setDriverClassName();
dataSource.setUrl(env.getProperty("app.database2.connection.url"));
// set all data source attributes from the application.properties file
return dataSource;
}
}
Now, somewhere in your code (either an Aspect or Controller), you need to dynamically set the data source conditionally:
DataSourceContextHolder.setDataSourceName("dataSource1");
Note: It's better to declare the data source names as enums rather than strings "dataSource1", "dataSource2", etc.
The below snippet works for me
first.datasource.jdbc-url=jdbc-url
first.datasource.username=username
first.datasource.password=password
.
.
.
.
=================== In Java Configuration File ==================
#Primary
#Bean(name = "firstDataSource")
#ConfigurationProperties(prefix = "first.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = "firstEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean barEntityManagerFactory(EntityManagerFactoryBuilder builder,
#Qualifier("firstDataSource") DataSource dataSource) {
Map<String, String> props = new HashMap<String, String>();
props.put("spring.jpa.database-platform", "org.hibernate.dialect.Oracle12cDialect");
.
.
.
return builder.dataSource(dataSource).packages("com.first.entity").persistenceUnit("firstDB")
.properties(props)
.build();
}
#Primary
#Bean(name = "firstTransactionManager")
public PlatformTransactionManager firstTransactionManager(
#Qualifier("firstEntityManagerFactory") EntityManagerFactory firstEntityManagerFactory) {
return new JpaTransactionManager(firstEntityManagerFactory);
}
second.datasource.jdbc-url=jdbc-url
second.datasource.username=username
second.datasource.password=password
.
.
.
.
=================== In Java Configuration File ==================
#Bean(name = "secondDataSource")
#ConfigurationProperties(prefix = "second.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "secondEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean barEntityManagerFactory(EntityManagerFactoryBuilder builder,
#Qualifier("secondDataSource") DataSource dataSource) {
Map<String, String> props = new HashMap<String, String>();
props.put("spring.jpa.database-platform", "org.hibernate.dialect.Oracle12cDialect");
.
.
.
return builder.dataSource(dataSource).packages("com.second.entity").persistenceUnit("secondDB")
.properties(props)
.build();
}
#Bean(name = "secondTransactionManager")
public PlatformTransactionManager secondTransactionManager(
#Qualifier("secondEntityManagerFactory") EntityManagerFactory secondEntityManagerFactory) {
return new JpaTransactionManager(secondEntityManagerFactory);
}

Resources