Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Configuring Multiple Data Sources with JPA in Spring Boot Applications

Tech Oct 24 1

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);
    }
}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.