在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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章