Posted on: January 19, 2025 Posted by: rahulgite Comments: 0

Spring Boot supports configuring multiple data sources in a single application. This is useful when working with different databases or schemas. Below is a step-by-step guide to set up multiple data sources.


1. Add Dependencies

Include the necessary dependencies for Spring Data JPA and your database drivers in pom.xml (for Maven projects):

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

2. Define Configuration in application.properties or application.yml

Example in application.properties:

# Primary DataSource
spring.datasource.primary.url=jdbc:mysql://localhost:3306/primarydb
spring.datasource.primary.username=root
spring.datasource.primary.password=password
spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver

# Secondary DataSource
spring.datasource.secondary.url=jdbc:mysql://localhost:3306/secondarydb
spring.datasource.secondary.username=root
spring.datasource.secondary.password=password
spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver

Example in application.yml:

spring:
  datasource:
    primary:
      url: jdbc:mysql://localhost:3306/primarydb
      username: root
      password: password
      driver-class-name: com.mysql.cj.jdbc.Driver
    secondary:
      url: jdbc:mysql://localhost:3306/secondarydb
      username: root
      password: password
      driver-class-name: com.mysql.cj.jdbc.Driver

3. Configure DataSource Beans

Define separate DataSource, EntityManagerFactory, and TransactionManager beans for each database.

Example Configuration for Primary DataSource:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
    basePackages = "com.example.primary.repository",
    entityManagerFactoryRef = "primaryEntityManagerFactory",
    transactionManagerRef = "primaryTransactionManager"
)
public class PrimaryDataSourceConfig {

    @Primary
    @Bean(name = "primaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Primary
    @Bean(name = "primaryEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(EntityManagerFactoryBuilder builder) {
        return builder
                .dataSource(primaryDataSource())
                .packages("com.example.primary.entity")
                .persistenceUnit("primary")
                .build();
    }

    @Primary
    @Bean(name = "primaryTransactionManager")
    public PlatformTransactionManager primaryTransactionManager(
            @Qualifier("primaryEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory);
    }
}

Example Configuration for Secondary DataSource:

@Configuration
@EnableJpaRepositories(
    basePackages = "com.example.secondary.repository",
    entityManagerFactoryRef = "secondaryEntityManagerFactory",
    transactionManagerRef = "secondaryTransactionManager"
)
public class SecondaryDataSourceConfig {

    @Bean(name = "secondaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "secondaryEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean secondaryEntityManagerFactory(EntityManagerFactoryBuilder builder) {
        return builder
                .dataSource(secondaryDataSource())
                .packages("com.example.secondary.entity")
                .persistenceUnit("secondary")
                .build();
    }

    @Bean(name = "secondaryTransactionManager")
    public PlatformTransactionManager secondaryTransactionManager(
            @Qualifier("secondaryEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory);
    }
}

4. Create Entities for Each DataSource

Define separate entities for each database.

Example for Primary Database:

@Entity
@Table(name = "users")
public class PrimaryUser {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    // Getters and Setters
}

Example for Secondary Database:

The SecondaryOrder class is mapped to the secondary database via the secondaryEntityManagerFactory configuration. This ensures that all operations on SecondaryOrder go through the secondary database.

@Entity
@Table(name = "orders")
public class SecondaryOrder {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String description;

    // Getters and Setters
}

5. Create Repositories

Define separate repository interfaces for each database.

Example for Primary Database:

@Repository
public interface PrimaryUserRepository extends JpaRepository<PrimaryUser, Long> {
}

Example for Secondary Database:

@Repository
public interface SecondaryOrderRepository extends JpaRepository<SecondaryOrder, Long> {
}

6. Using Multiple DataSources

Inject the repositories to use them in your service or controller.

Example Service:

@Service
public class MultiDataSourceService {

    private final PrimaryUserRepository primaryUserRepository;
    private final SecondaryOrderRepository secondaryOrderRepository;

    public MultiDataSourceService(PrimaryUserRepository primaryUserRepository, SecondaryOrderRepository secondaryOrderRepository) {
        this.primaryUserRepository = primaryUserRepository;
        this.secondaryOrderRepository = secondaryOrderRepository;
    }

    public void performDatabaseOperations() {
        // Operations on primary database
        PrimaryUser user = new PrimaryUser();
        user.setName("John Doe");
        primaryUserRepository.save(user);

        // Operations on secondary database
        SecondaryOrder order = new SecondaryOrder();
        order.setDescription("Sample Order");
        secondaryOrderRepository.save(order);
    }
}

7. Best Practices

  1. Use Descriptive Names: Name your beans and configurations descriptively (e.g., primaryDataSource).
  2. Separate Entities: Use different packages for entities belonging to different data sources.
  3. Centralized Configuration: Keep data source configurations in application.properties or application.yml for easier management.
  4. Test Thoroughly: Ensure queries and transactions work as expected for both data sources.

This setup allows seamless integration of multiple databases in a Spring Boot application.

Leave a Comment