Configuring Multiple Data Sources with JPA in Spring Boot Applications
Configuration File Setup
Define both data source connections in application.properties:
# Primary Data Source
spring.datasource.primary.jdbc-url=jdbc:mysql://192.168.1.100:3306/userdb?useUnicode=true&characterEncoding=UTF-8&useSSL=false
spring.datasource.primary.username=admin
spring.datasource.primary.password=password123
spring.datasource.primary.driver-class-name=com.mysql.jdbc.Driver
# Secondary Data Source
spring.datasource.secondary.jdbc-url=jdbc:mysql://localhost:3306/logdb?useUnicode=true&characterEncoding=UTF-8&useSSL=false
spring.datasource.secondary.username=logger
spring.datasource.secondary.password=password456
spring.datasource.secondary.driver-class-name=com.mysql.jdbc.Driver
# Hibernate Settings
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql=true
DataSource Configuration Class
Create a configuration class to initialize both data sources at application startup:
@Configuration
public class DataSourceConfiguration {
@Bean(name = "primaryDataSource")
@Primary
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource() {
System.out.println("Primary database connection established");
return DataSourceBuilder.create().build();
}
@Bean(name = "secondaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSource secondaryDataSource() {
System.out.println("Secondary database connection established");
return DataSourceBuilder.create().build();
}
}
Primary Data Source Configuration
This configuration handles the main data source with its EntityManager and TransactionManager:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactoryPrimary",
transactionManagerRef = "transactionManagerPrimary",
basePackages = {"com.example.project.sys.repository", "com.example.project.common.repository"}
)
public class PrimaryDataSourceConfig {
@Autowired
private JpaProperties jpaProperties;
@Autowired
private HibernateProperties hibernateProperties;
@Autowired
@Qualifier("primaryDataSource")
private DataSource primaryDataSource;
@Primary
@Bean(name = "entityManagerPrimary")
public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
return entityManagerFactoryPrimary(builder).getObject().createEntityManager();
}
@Primary
@Bean(name = "entityManagerFactoryPrimary")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary(EntityManagerFactoryBuilder builder) {
Map<String, Object> properties = hibernateProperties.determineHibernateProperties(
jpaProperties.getProperties(), new HibernateSettings());
return builder
.dataSource(primaryDataSource)
.properties(properties)
.packages("com.example.project.sys.pojo", "com.example.project.common.pojo")
.persistenceUnit("primaryPersistenceUnit")
.build();
}
@Primary
@Bean(name = "transactionManagerPrimary")
public PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject());
}
}
Secondary Data Source Configuration
This configuration manages the secondary data source for auxilairy operations:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactorySecondary",
transactionManagerRef = "transactionManagerSecondary",
basePackages = {"com.example.project.logs.repository"}
)
public class SecondaryDataSourceConfig {
@Autowired
private JpaProperties jpaProperties;
@Autowired
private HibernateProperties hibernateProperties;
@Autowired
@Qualifier("secondaryDataSource")
private DataSource secondaryDataSource;
@Bean(name = "entityManagerSecondary")
public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
return entityManagerFactorySecondary(builder).getObject().createEntityManager();
}
@Bean(name = "entityManagerFactorySecondary")
public LocalContainerEntityManagerFactoryBean entityManagerFactorySecondary(EntityManagerFactoryBuilder builder) {
Map<String, Object> properties = hibernateProperties.determineHibernateProperties(
jpaProperties.getProperties(), new HibernateSettings());
return builder
.dataSource(secondaryDataSource)
.properties(properties)
.packages("com.example.project.logs.pojo")
.persistenceUnit("secondaryPersistenceUnit")
.build();
}
@Bean(name = "transactionManagerSecondary")
public PlatformTransactionManager transactionManagerSecondary(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(entityManagerFactorySecondary(builder).getObject());
}
}
Transaction Rollback Configuration
To ensure proper transaction rollback, specify the transaction manager explicitly using the value attribute:
@Service
public class DataProcessingService implements DataProcessingInterface {
@Autowired
private DataProcessingRepository dataProcessingRepository;
@Transactional(value = "transactionManagerSecondary", rollbackFor = Exception.class)
@Override
public OperationResult<Map<String, Object>> processData() {
Map<String, Object> result = new HashMap<>();
dataProcessingRepository.insertRecord1();
dataProcessingRepository.insertRecord2();
dataProcessingRepository.insertRecord3();
dataProcessingRepository.insertRecord4();
return OperationResult.success(result);
}
}