在 Spring Boot 中配置多数据源可以满足一些复杂业务场景的需求,例如同时访问多个数据库。下面将详细介绍两种常见的配置多数据源的方法:使用 JdbcTemplate 和使用 Spring Data JPA。

方法一:使用 JdbcTemplate 配置多数据源

1. 添加依赖

pom.xml 中添加必要的依赖,以 MySQL 数据库为例:

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

2. 配置数据源信息

application.propertiesapplication.yml 中配置多个数据源的信息:

# 第一个数据源
spring.datasource.first.url=jdbc:mysql://localhost:3306/db1
spring.datasource.first.username=root
spring.datasource.first.password=password
spring.datasource.first.driver-class-name=com.mysql.cj.jdbc.Driver

# 第二个数据源
spring.datasource.second.url=jdbc:mysql://localhost:3306/db2
spring.datasource.second.username=root
spring.datasource.second.password=password
spring.datasource.second.driver-class-name=com.mysql.cj.jdbc.Driver

3. 配置数据源 Bean

创建一个配置类,用于配置多个数据源和对应的 JdbcTemplate

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;

@Configuration
public class DataSourceConfig {

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

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

    @Bean(name = "firstJdbcTemplate")
    public JdbcTemplate firstJdbcTemplate(@Qualifier("firstDataSource") DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

    @Bean(name = "secondJdbcTemplate")
    public JdbcTemplate secondJdbcTemplate(@Qualifier("secondDataSource") DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}

在上述代码中,使用 @ConfigurationProperties 注解将配置文件中的数据源信息绑定到数据源 Bean 上,使用 @Primary 注解指定主数据源。同时,为每个数据源创建对应的 JdbcTemplate Bean。

4. 使用多数据源

在服务类中注入不同的 JdbcTemplate 并使用:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

@Service
public class MultiDataSourceService {

    @Autowired
    @Qualifier("firstJdbcTemplate")
    private JdbcTemplate firstJdbcTemplate;

    @Autowired
    @Qualifier("secondJdbcTemplate")
    private JdbcTemplate secondJdbcTemplate;

    public void useFirstDataSource() {
        String sql = "SELECT COUNT(*) FROM table1";
        Integer count = firstJdbcTemplate.queryForObject(sql, Integer.class);
        System.out.println("第一个数据源的记录数: " + count);
    }

    public void useSecondDataSource() {
        String sql = "SELECT COUNT(*) FROM table2";
        Integer count = secondJdbcTemplate.queryForObject(sql, Integer.class);
        System.out.println("第二个数据源的记录数: " + count);
    }
}

方法二:使用 Spring Data JPA 配置多数据源

1. 添加依赖

pom.xml 中添加必要的依赖:

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

2. 配置数据源信息

同样在 application.propertiesapplication.yml 中配置多个数据源的信息,与使用 JdbcTemplate 时的配置相同。

3. 配置数据源和 JPA 相关 Bean

创建两个配置类,分别配置两个数据源和对应的 JPA 相关 Bean:

第一个数据源配置类

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.util.HashMap;

@Configuration
@EnableJpaRepositories(
        entityManagerFactoryRef = "firstEntityManagerFactory",
        transactionManagerRef = "firstTransactionManager",
        basePackages = {"com.example.demo.repository.first"}
)
public class FirstDataSourceConfig {

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

    @Bean(name = "firstEntityManagerFactory")
    @Primary
    public LocalContainerEntityManagerFactoryBean firstEntityManagerFactory(
            @Qualifier("firstDataSource") DataSource dataSource) {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(dataSource);
        em.setPackagesToScan("com.example.demo.entity.first");

        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);

        HashMap<String, Object> properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto", "update");
        properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL8Dialect");
        em.setJpaPropertyMap(properties);

        return em;
    }

    @Bean(name = "firstTransactionManager")
    @Primary
    public PlatformTransactionManager firstTransactionManager(
            @Qualifier("firstEntityManagerFactory") LocalContainerEntityManagerFactoryBean entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory.getObject());
    }
}

第二个数据源配置类

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.util.HashMap;

@Configuration
@EnableJpaRepositories(
        entityManagerFactoryRef = "secondEntityManagerFactory",
        transactionManagerRef = "secondTransactionManager",
        basePackages = {"com.example.demo.repository.second"}
)
public class SecondDataSourceConfig {

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

    @Bean(name = "secondEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean secondEntityManagerFactory(
            @Qualifier("secondDataSource") DataSource dataSource) {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(dataSource);
        em.setPackagesToScan("com.example.demo.entity.second");

        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);

        HashMap<String, Object> properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto", "update");
        properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL8Dialect");
        em.setJpaPropertyMap(properties);

        return em;
    }

    @Bean(name = "secondTransactionManager")
    public PlatformTransactionManager secondTransactionManager(
            @Qualifier("secondEntityManagerFactory") LocalContainerEntityManagerFactoryBean entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory.getObject());
    }
}

4. 创建实体类和 Repository 接口

分别为两个数据源创建对应的实体类和 Repository 接口,并将它们放在不同的包下,与配置类中的 basePackages 对应。

第一个数据源的实体类和 Repository 接口示例

// 实体类
package com.example.demo.entity.first;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class FirstEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    // 省略 getter 和 setter 方法
}

// Repository 接口
package com.example.demo.repository.first;

import com.example.demo.entity.first.FirstEntity;
import org.springframework.data.jpa.repository.JpaRepository;

public interface FirstRepository extends JpaRepository<FirstEntity, Long> {
}

第二个数据源的实体类和 Repository 接口示例

// 实体类
package com.example.demo.entity.second;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class SecondEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String description;

    // 省略 getter 和 setter 方法
}

// Repository 接口
package com.example.demo.repository.second;

import com.example.demo.entity.second.SecondEntity;
import org.springframework.data.jpa.repository.JpaRepository;

public interface SecondRepository extends JpaRepository<SecondEntity, Long> {
}

5. 使用多数据源

在服务类中注入不同的 Repository 并使用:

import com.example.demo.repository.first.FirstRepository;
import com.example.demo.repository.second.SecondRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class MultiDataSourceJpaService {

    @Autowired
    private FirstRepository firstRepository;

    @Autowired
    private SecondRepository secondRepository;

    public void useFirstDataSource() {
        long count = firstRepository.count();
        System.out.println("第一个数据源的记录数: " + count);
    }

    public void useSecondDataSource() {
        long count = secondRepository.count();
        System.out.println("第二个数据源的记录数: " + count);
    }
}

通过以上两种方法,你可以在 Spring Boot 中配置和使用多数据源。使用 JdbcTemplate 适用于简单的 SQL 操作,而使用 Spring Data JPA 则更适合于基于对象的持久化操作。