欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SpringBoot多数据源配置和使用

程序员文章站 2022-07-14 09:18:23
...

SpringBoot中使用JPA如果是单数据源,很好处理,只需要在配置文件中配置连接参数和性能参数即可。如下:

username、password、url、driver-class-name等配置省略。。。
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.initialSize=10
spring.datasource.minIdle=20
spring.datasource.maxActive=300
spring.datasource.maxWait=30000
spring.datasource.timeBetweenEvictionRunsMillis=120000
spring.datasource.minEvictableIdleTimeMillis=120000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
spring.datasource.poolPreparedStatements=true
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20

spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

但是多数据源就没这么简单了,需要我们自己配置数据源、EntityManager、TransactionManager等,其中一般地应该有个主数据源。

首先是配置数据源,配置文件中配置以spring.datasource.primary和spring.datasource.AAAAdb。。。开头的连接配置,如果每一个数据源的性能参数不一样,也以这些开头设置即可,createDruidDataSource就不需要设置了。

@Configuration
public class DataSourceConfig {

    private static final Logger logger = LoggerFactory.getLogger(DataSourceConfig.class);

    @Bean(name = Constant.PRIMARY_DATASOURCE)
    @Qualifier(Constant.PRIMARY_DATASOURCE)
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
        DataSource dataSource = createMySqlDataSource();
        logger.info("primary datasource = " + dataSource);
        return dataSource;
    }

    
    @Bean(name = Constant.AAAA_DB_DATASOURCE)
    @Qualifier(Constant.AAAA_DB_DATASOURCE)
    @ConfigurationProperties(prefix = "spring.datasource.AAAAdb")
    public DataSource AAAAdbDataSource() {
        DataSource dataSource = createOracleDataSource();
        logger.info("AAAA datasource = " + dataSource);
        return dataSource;
    }

    @Bean(name = Constant.BBBB_DB_DATASOURCE)
    @Qualifier(Constant.BBBB_DB_DATASOURCE)
    @ConfigurationProperties(prefix = "spring.datasource.BBBBdb")
    public DataSource BBBBdbDataSource() {
        DataSource dataSource = createOracleDataSource();
        logger.info("BBBB datasource = " + dataSource);
        return dataSource;
    }

    @Bean(name = Constant.CCCC_DB_DATASOURCE)
    @Qualifier(Constant.CCCC_DB_DATASOURCE)
    @ConfigurationProperties(prefix = "spring.datasource.CCCCdb")
    public DataSource CCCCdbDataSource() {
        DataSource dataSource = createOracleDataSource();
        logger.info("CCCC datasource = " + dataSource);
        return dataSource;
    }


    @Value("${spring.datasource.initialSize}")
    private int initialSize;

    @Value("${spring.datasource.maxActive}")
    private int maxActive;

    @Value("${spring.datasource.maxWait}")
    private int maxWait;

    @Value("${spring.datasource.timeBetweenEvictionRunsMillis}")
    private int timeBetweenEvictionRunsMillis;

    @Value("${spring.datasource.minEvictableIdleTimeMillis}")
    private int minEvictableIdleTimeMillis;

    @Value("${spring.datasource.validationQuery}")
    private String validationQuery;

    @Value("${spring.datasource.driver-class-name}")
    private String driverClassName;

    @Value("${spring.datasource.testWhileIdle}")
    private boolean testWhileIdle;

    @Value("${spring.datasource.poolPreparedStatements}")
    private boolean poolPreparedStatements;

    @Value("${spring.datasource.maxPoolPreparedStatementPerConnectionSize}")
    private int maxPoolPreparedStatementPerConnectionSize;

    @Value("${spring.datasource.testOnBorrow}")
    private boolean testOnBorrow;

    @Value("${spring.datasource.testOnReturn}")
    private boolean testOnReturn;

    private DataSource createMySqlDataSource() {
        DruidDataSource druidDataSource = createDruidDataSource();
        logger.info("driverClassName : " + driverClassName);
        //注意oracle不能这做
        druidDataSource.setDriverClassName(driverClassName);
        return druidDataSource;
    }

    private DataSource createOracleDataSource() {
        DruidDataSource druidDataSource = createDruidDataSource();
        return druidDataSource;
    }

    /**因为4个数据源大部分属性都一致,如果要完全通过配置文件的方式,要写很多行
    为了简化,这里采用配置文件+代码的方式设置属性*/
    private DruidDataSource createDruidDataSource() {
        logger.info("maxActive : {},initialSize : {},maxWait : {}," +
                "timeBetweenEvictionRunsMillis : {},minEvictableIdleTimeMillis : {},validationQuery : {}," +
                "testWhileIdle : {},poolPreparedStatements : {},maxPoolPreparedStatementPerConnectionSize : {}," +
                "testOnBorrow : {},testOnReturn : {}" , maxActive , initialSize , maxWait , timeBetweenEvictionRunsMillis,
                minEvictableIdleTimeMillis , validationQuery , testWhileIdle , poolPreparedStatements , maxPoolPreparedStatementPerConnectionSize ,
                testOnBorrow , testOnReturn);

        DataSourceBuilder builder = DataSourceBuilder.create().type(DruidDataSource.class);
        DruidDataSource druidDataSource = (DruidDataSource) builder.build();
        druidDataSource.setMaxActive(maxActive);
        druidDataSource.setInitialSize(initialSize);
        druidDataSource.setMaxWait(maxWait);
        druidDataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
        druidDataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        druidDataSource.setValidationQuery(validationQuery);
        druidDataSource.setTestWhileIdle(testWhileIdle);
        druidDataSource.setPoolPreparedStatements(poolPreparedStatements);
        druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
        druidDataSource.setTestOnBorrow(testOnBorrow);
        druidDataSource.setTestOnReturn(testOnReturn);
        return druidDataSource;
    }


}

主数据源配置

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
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.Database;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.persistence.EntityManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = PrimaryConfig.ENTITY_MANAGER_FACTORY_PRIMARY,
        transactionManagerRef = PrimaryConfig.TRANSACTION_MANAGER_PRIMARY,
        basePackages = {Constant.PRIMARY_DATA_PACKAGE}) //设置Repository所在位置
public class PrimaryConfig {

    public static final String TRANSACTION_MANAGER_PRIMARY    = "transactionManagerPrimary";
    public static final String ENTITY_MANAGER_FACTORY_PRIMARY = "entityManagerFactoryPrimary";

    @Value("${primary.hibernate.dialect}")
    private String primaryDialect;

    @Autowired
    @Qualifier(Constant.PRIMARY_DATASOURCE)
    private DataSource primaryDataSource;

    @Primary
    @Bean(name = Constant.ENTITY_MANAGER_PRIMARY_DB)
    public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
        return entityManagerFactoryPrimary(builder).getObject().createEntityManager();
    }

    @Primary
    @Bean(name = ENTITY_MANAGER_FACTORY_PRIMARY)
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary(EntityManagerFactoryBuilder builder) {
        return builder
                .dataSource(primaryDataSource)
                .properties(getVendorProperties(primaryDataSource))
                //设置实体类所在位置
                .packages(Constant.PRIMARY_DATA_PACKAGE)
                .persistenceUnit(Constant.PRIMARY_PERSISTENCE_UNIT)
                .build();
    }

    @Autowired
    private JpaProperties jpaProperties;

    private Map<String, String> getVendorProperties(DataSource dataSource) {
        jpaProperties.setDatabase(Database.MYSQL);
        Map<String,String> map = new HashMap<>();
        map.put("hibernate.dialect", primaryDialect);
        System.out.println("primary " + primaryDialect);
        jpaProperties.setProperties(map);
        return jpaProperties.getHibernateProperties(dataSource);
    }

    @Primary
    @Bean(name = TRANSACTION_MANAGER_PRIMARY)
    public PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) {
        return new JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject());
    }

}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
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.Database;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.persistence.EntityManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = AAAAdbConfig.ENTITY_MANAGER_FACTORY_AAAA_DB,
        transactionManagerRef = AAAAdbConfig.TRANSACTION_MANAGER_AAAA_DB,
        basePackages = {Constant.AAAADB_DATA_PACKAGE}) //设置Repository所在位置
public class AAAAdbConfig {

    @Value("${boss.hibernate.dialect}")
    private String bossDialect;

    public static final String TRANSACTION_MANAGER_AAAA_DB    = "transactionManagerAAAAdb";
    public static final String ENTITY_MANAGER_FACTORY_AAAA_DB = "entityManagerFactoryAAAAdb";

    @Autowired
    @Qualifier(Constant.AAAA_DB_DATASOURCE)
    private DataSource AAAAdbDataSource;

    @Bean(name = Constant.ENTITY_MANAGER_AAAA_DB)
    public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
        return entityManagerFactory(builder).getObject().createEntityManager();
    }

    @Bean(name = ENTITY_MANAGER_FACTORY_AAAA_DB)
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder) {
        return builder
                .dataSource(AAAAdbDataSource)
                .properties(getVendorProperties(AAAAdbDataSource))
                //设置实体类所在位置
                .packages(Constant.AAAADB_DATA_PACKAGE)
                .persistenceUnit("AAAAdbPersistenceUnit")
                .build();
    }

    @Autowired
    private JpaProperties jpaProperties;

    private Map<String, String> getVendorProperties(DataSource dataSource) {
        jpaProperties.setDatabase(Database.ORACLE);
        Map<String,String> map = new HashMap<>();
        map.put("hibernate.dialect", bossDialect);
        System.out.println("AAAA " +  bossDialect);
        jpaProperties.setProperties(map);
        return jpaProperties.getHibernateProperties(dataSource);
    }

    @Bean(name = TRANSACTION_MANAGER_AAAA_DB)
    PlatformTransactionManager transactionManager(EntityManagerFactoryBuilder builder) {
        return new JpaTransactionManager(entityManagerFactory(builder).getObject());
    }
}

其他几个类似配置即可。

参数名字很多地方一样,所以设置为常量

public class Constant {

    public static final String PRIMARY_DATASOURCE        = "primaryDataSource";
    public static final String ENTITY_MANAGER_PRIMARY_DB = "entityManagerPrimary";
    public static final String PRIMARY_PAGINATION        = "pagination";

    public static final String AAAA_DB_DATASOURCE = "AAAAdbDataSource";
    public static final String BBBB_DB_DATASOURCE = "BBBBdbDataSource";
    public static final String CCCC_DB_DATASOURCE = "CCCCdbDataSource";

    public static final String ENTITY_MANAGER_AAAA_DB = "entityManagerAAAAdb";
    public static final String ENTITY_MANAGER_BBBB_DB = "entityManagerBBBBdb";
    public static final String ENTITY_MANAGER_CCCC_DB = "entityManagerCCCCdb";


    public static final String AAAA_PAGINATION  = "AAAAPagination";
    public static final String BBBB_PAGINATION  = "BBBBPagination";
    public static final String CCCC_PAGINATION  = "CCCCPagination";


    ///在使用Pagination的时候需要注入EntityManager,就要使用@PersistenceContext(unitName=PRIMARY_PERSISTENCE_UNIT)
    ///其他数据源没得问题,只是对主数据源有问题,具体原因未知
    /**
         @PersistenceContext(unitName = Constant.PRIMARY_PERSISTENCE_UNIT)
         @Autowired
         @Qualifier(Constant.ENTITY_MANAGER_PRIMARY_DB)
         private EntityManager entityManager;
     */
    public static final String PRIMARY_PERSISTENCE_UNIT = "primaryPersistenceUnit";
}

每个数据源的包必须不同(包括Entity和Repository)。如此,就可以正常使用各种Repository了。

但是由于业务的复杂性,我们可能需要动态生成SQL语句,并查询,参照上篇博文的Pagination,我们需要给Pagination注入一个EntityManager,对于一般的数据源,直接注入相应名字的即可,但是对于主数据源则需要特殊处理,(其实我感觉都应该像后者一样处理,没仔细测试)。

import cn.palmte.gpas.utils.Pagination;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

/**
 * @author xiongshiyan at 2018/7/23 , contact me with email [email protected] or phone 15208384257
 */
@Configuration
public class PaginationConfig {

    @PersistenceContext(unitName = Constant.PRIMARY_PERSISTENCE_UNIT)
    @Autowired
    @Qualifier(Constant.ENTITY_MANAGER_PRIMARY_DB)
    private EntityManager entityManagerPrimary;

    ///错误的注入方式,可能发生数据库8小时问题
    /*@Primary
    @Bean(Constant.PRIMARY_PAGINATION)
    public Pagination primary(@Qualifier(Constant.ENTITY_MANAGER_PRIMARY_DB) EntityManager entityManager){
        return new Pagination(entityManager);
    }*/
    @Primary
    @Bean(Constant.PRIMARY_PAGINATION)
    public Pagination primary(){
        return new Pagination(entityManagerPrimary);
    }
    @Bean(Constant.AAAA_PAGINATION)
    public Pagination AAAA(@Qualifier(Constant.ENTITY_MANAGER_AAAA_DB) EntityManager entityManager){
        return new Pagination(entityManager);
    }
    @Bean(Constant.BBBB_PAGINATION)
    public Pagination BBBB(@Qualifier(Constant.ENTITY_MANAGER_BBBB_DB) EntityManager entityManager){
        return new Pagination(entityManager);
    }
    @Bean(Constant.CCCC_PAGINATION)
    public Pagination CCCC(@Qualifier(Constant.ENTITY_MANAGER_CCCC_DB) EntityManager entityManager){
        return new Pagination(entityManager);
    }
}

最重要的是使用@PersistenceContext注解EntityManager,并制定相应的unitName。

这样,我们就可以注入相应的Pagination使用了。

参考:

一个JPA环境下原生SQL查询的工具类

 

 

 

相关标签: JPA Pagination