在Spring JPA中配置多数据源

http://tommwq.tech/blog/2020/12/04/262

在Spring JPA中配置多个数据源并不复杂,只需要为每个数据源声明一个@EnableJpaRepositories。

如果程序只使用一个数据源,通常在入口类添加注解@EnableJpaRepositories,不需要任何参数。然后写好配置spring.datasource.*就可以了。但是如果需要多个数据源,就要为每个数据源声明一个@EnableJpaRepositories。@EnableJpaRepositoreis根据包名进行扫描,因此不同数据源使用的Entity和Repository需要分别保存到不同的包中。为了便于管理,建议为@EnableJpaRepositories编写一个单独的配置类,和Entity、Repository放在同一个包里。

Listing 1: 包结构

com.tommwq.test.foo
    FooEntity
    FooRepository
    FooConfiguration
com.tommwq.test.bar
    BarEntity
    BarRepository
    BarConfiguration

使用多个数据源时,Entity和Repository不用修改。只要设置好@EnableJpaRepositories,就能让Repository找到正确的数据源,自动装配。其中主要的两个参数是创建Entity的entityManagerFactoryRef和创建Repository的transactionManagerRef。为了创建EntityManagerFactory,我们需要借助DataSourceProperties创建DataSource对象。而使用@ConfigurationProperties,可以自动从配置生成DataSourceProperties。创建TransactionManager则需要指定SQL方言。方言类名可以通过Environment从配置中读取。

Listing 2: 数据源配置类

package com.tommwq.test.foo;

import javax.sql.*;
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.core.env.*;
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.*;
import org.springframework.transaction.PlatformTransactionManager;

@Configuration
@EnableJpaRepositories
        entityManagerFactoryRef="fooEntityManagerFactory",
        transactionManagerRef="fooTransactionManager")
public class FooConfiguration {
    @Autowired
    Environment env;

    @Bean
    @ConfigurationProperties(prefix="foo.datasource")
    public DataSourceProperties fooDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    public DataSource fooDataSource() {
        return fooDataSourceProperties()
            .initializeDataSourceBuilder()
            .type(BasicDataSource.class)  // 使用了 apache DBCH2
            .build();
    }

    @Bean(name="fooEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean fooEntityManagerFactory(EntityManagerFactoryBuilder builder) {
        return builder.dataSource(fooDataSource())
            .packages("com.tommwq.test.foo")
            .build();
    }

    @Bean(name="fooTransactionManager")
    public PlatformTransactionManager fooTransactionManager(@Qualifier("fooEntityManagerFactory") LocalContainerEntityManagerFactoryBean bean) {
        JpaTransactionManager manager = new JpaTransactionManager(bean.getObject());
        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setDatabasePlatform(env.getProperty("foo.datasource.dialect"));

        manager.setJpaDialect(vendorAdapter.getJpaDialect());
        return manager;
    }
}

这就是一个数据源配置类的完整代码。每个数据源都要写一个这样的类。其中一个配置类中的Bean要标记为@Primary。代码看起来很多,实际上大部分是固定的,可以用模板来生成,变化的只有3个参数:

packageName
dataSourceName
ConfigurationClassName

下面是采用FreeMarker编写的模板。

package ${packageName};

import javax.sql.*;
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.core.env.*;
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.*;
import org.springframework.transaction.PlatformTransactionManager;

@Configuration
@EnableJpaRepositories
        entityManagerFactoryRef="${dataSourceName}EntityManagerFactory",
        transactionManagerRef="${dataSourceName}TransactionManager")
public class ${configurationClassName} {
    @Autowired
    Environment env;

    @Bean
    @ConfigurationProperties(prefix="${dataSourceName}.datasource")
    public DataSourceProperties ${dataSourceName}DataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean
    public DataSource ${dataSourceName}DataSource() {
        return ${dataSourceName}DataSourceProperties()
            .initializeDataSourceBuilder()
            .type(BasicDataSource.class)  // 使用了 apache DBCH2
            .build();
    }

    @Bean(name="${dataSourceName}EntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean ${dataSourceName}EntityManagerFactory(EntityManagerFactoryBuilder builder) {
        return builder.dataSource(${dataSourceName}DataSource())
            .packages("${packageName}")
            .build();
    }

    @Bean(name="${dataSourceName}TransactionManager")
    public PlatformTransactionManager ${dataSourceName}TransactionManager(@Qualifier("${dataSourceName}EntityManagerFactory") LocalContainerEntityManagerFactoryBean bean) {
        JpaTransactionManager manager = new JpaTransactionManager(bean.getObject());
        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setDatabasePlatform(env.getProperty("${dataSourceName}.datasource.dialect"));

        manager.setJpaDialect(vendorAdapter.getJpaDialect());
        return manager;
    }
}

Listing 3: 配置

foo.datasource.driverClassName=com.mysql.jdbc.Driver
foo.datasource.url=jdbc:mysql://localhost:3306/test
foo.datasource.username=root
foo.datasource.password=myPassword
foo.datasource.dialect=org.hibernate.dialect.MySQL5InnoDBDialect

猜你喜欢

转载自blog.csdn.net/tq1086/article/details/110674848