zoukankan      html  css  js  c++  java
  • SpringBoot多数据库连接(mysql+oracle)

    出于业务需求,有时我们需要在spring boot web应用程序中配置多个数据源并连接到多个数据库。
    使用过Spring Boot框架的小伙伴们,想必都发现了Spring Boot对JPA提供了非常好的支持,在开发过程中可以很简洁的代码轻松访问数据库,获取我们想要的数据。
    因此在这里,使用Spring Boot和JPA配置多个数据源的场景。

    项目配置

    在本文中,主要使用两个不同的数据库,分别为:
    • mysql(springboot)【primary,优先搜寻该数据库】:mysql数据库,包含User的信息
    • oracle(springboot): oracle数据库, 包含Country信息

    项目依赖

    为了支持Mysql和Oracle数据库,我们必须要在pom.xml文件中添加相应的依赖。

    <dependencies>
        <dependency>
           <groupId>com.oracle</groupId>
           <artifactId>ojdbc6</artifactId>
           <version>11.2.0.3.0</version>
           <scope>compile</scope>
        </dependency>
        <dependency>
           <groupId>mysql</groupId>
           <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
           <groupId>javax.persistence</groupId>
           <artifactId>javax.persistence-api</artifactId>
           <version>2.2</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
    </dependencies> 

    包管理

    为了方便代码的开发,我们将mysql和oracle分开放在两个不同的package下面,具体的包结构如下:

    将不同的模型分开放入mysql和oracle包目录下,需要注意的是,mysql和oracle为两个不同的数据库,所以两个数据库中可能存在某个表名称一致的场景。该场景下,会优先匹配primary的数据库,如果该数据库down了,才会匹配另外一张表。所以,如果想要两张表都正常使用,建议使用不同的Entity名称。

    数据库连接配置

    我们在属性文件application.properties中分别配置两个单独的jdbc连接,将所有关联的Entity类和Repository映射到两个不同的包中。

    ## jdbc-primary
    spring.datasource.url=jdbc:mysql://localhost:33306/springboot?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false
    spring.datasource.username=springboot
    spring.datasource.password=123456
    spring.ds_mysql.driverClassName=com.mysql.jdbc.Driver
    
    ## jdbc-second
    spring.second.datasource.url=jdbc:oracle:thin:@localhost:1909/xxx.xxx.com
    spring.second.datasource.userName=springboot
    spring.second.datasource.password=123456
    spring.second.datasource.driver-class-name=oracle.jdbc.OracleDriver
    
    ## jpa
    spring.jpa.hibernate.ddl-auto=none
    spring.jpa.show-sql=true
    spring.jpa.properties.hibernate.jdbc.time_zone=UTC
    spring.jpa.properties.hibernate.jdbc.fetch_size=500
    spring.jpa.properties.hibernate.jdbc.batch_size=100

    数据源配置

    需要注意的是,在配置多个数据源期间,必须将其中一个数据源标记为primary,否则Spring Boot会检测到多个类型的数据源,从而无法正常启动。

    定义Data Source的Bean

    想要创建Data Source,我们必须先实例化org.springframework.boot.autoconfigure.jdbc.DataSourceProperties类,加载application.properties文件中配置的数据库连接信息,并通过DataSourceProperties对象的初始化builder方法创建一个javax.sql.DataSource对象。
    primary Data Source

    @Primary
    @Bean(name = "mysqlDataSourceProperties")
    @ConfigurationProperties("spring.datasource")
    public DataSourceProperties dataSourceProperties() {
        return new DataSourceProperties();
    }
    
    @Primary
    @Bean(name = "mysqlDataSource")
    @ConfigurationProperties("spring.datasource.configuration")
    public DataSource dataSource (@Qualifier("mysqlDataSourceProperties") DataSourceProperties mysqlDataSourceProperties) {
        return mysqlDataSourceProperties.initializeDataSourceBuilder()
                .type(HikariDataSource.class)
                .build();
    }
    Secondary Data Source
    @Bean(name = "oracleDataSourceProperties")
    @ConfigurationProperties("spring.second.datasource")
    public DataSourceProperties dataSourceProperties() {
        return new DataSourceProperties();
    }
    
    @Bean
    @ConfigurationProperties("spring.second.datasource.configuration")
    public DataSource oracleDataSource(@Qualifier("oracleDataSourceProperties") DataSourceProperties oracleDataSourceProperties) {
        return oracleDataSourceProperties.initializeDataSourceBuilder()
                .type(HikariDataSource.class)
                .build();
    }

    我们使用@Qualifier注解,自动关联指定的DataSourceProperties.

    定义实体类管理工厂的Bean
    上面说了,应用程序使用Spring Data JPA的repository接口将我们从实体管理器(Entity Manager)中抽象出来,从而进行数据的访问。这里,我们使用org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean这个Bean来创建EM实例,后面利用这个EM实例与JPA entities进行交互。
    由于我们这里有两个数据源,所以我要为每个数据源单独创建一个EntityManagerFactory。
    Primary Entity Manager Factory
    @Primary
    @Bean(name = "mysqlEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(
            EntityManagerFactoryBuilder builder, @Qualifier("mysqlDataSource") DataSource mysqlDataSource) {
        return builder.dataSource(mysqlDataSource)
                .packages("com.example.demo.model.mysql")
                .persistenceUnit("mysql")
                .build();
    }
    Secondary Entity Manager Factory

    @Bean(name = "oracleEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean oracleEntityManagerFactory(
            EntityManagerFactoryBuilder builder, @Qualifier("oracleDataSource") DataSource oracleDataSource) {
        return builder.dataSource(oracleDataSource)
                .packages("com.example.demo.model.oracle")
                .persistenceUnit("oracle")
                .build();
    }
    我们使用@Qualifie注解,自动将DataSource关联到对应的EntityManangerFactory中。
    在这里我们可以分别配置实体类管理工厂所管理的packages,为了方便开发和阅读,分别将mysql和oracle关联的实体类放在对应的目录下。

    事务管理

    我们为每个数据库创建一个JPA事务管理器。
    查看源码我们可以发现JPA事务管理器需要EntityManangerFactory作为入参,所以利用上述定义的EntityMangerFactory分别生成对应的JPA事物管理器。
    源码:

    public JpaTransactionManager(EntityManagerFactory emf) {
        this();
        this.entityManagerFactory = emf;
        this.afterPropertiesSet();
    }
    Primary transaction manager

    @Primary
    @Bean(name = "mysqlTransactionManager")
    public PlatformTransactionManager mysqlTransactionManager(final @Qualifier("mysqlEntityManagerFactory")
                                                                     LocalContainerEntityManagerFactoryBean mysqlEntityManagerFactory) {
        return new JpaTransactionManager(mysqlEntityManagerFactory.getObject());
    }
    Secondary transaction manager

    @Bean(name = "oracleTransactionManager")
    public PlatformTransactionManager oracleTransactionManager(
            final @Qualifier("oracleEntityManagerFactory")
                    LocalContainerEntityManagerFactoryBean oracleEntityManagerFactory) {
        return new JpaTransactionManager(oracleEntityManagerFactory.getObject());
    }

    JPA Repository配置

    由于我们使用了两个不同的数据源,所以我们必须使用@EnableJpaRepositories注解为每个数据源提供特定的信息。
    进入该注解源码,我们可以发现默认值如下:

    /**
     * Annotation to enable JPA repositories. Will scan the package of the annotated configuration class for Spring Data
     * repositories by default.
     *
     * @author Oliver Gierke
     * @author Thomas Darimont
     */
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Import(JpaRepositoriesRegistrar.class)
    public @interface EnableJpaRepositories {
        /**
         * Base packages to scan for annotated components. {@link #value()} is an alias for (and mutually exclusive with) this
         * attribute. Use {@link #basePackageClasses()} for a type-safe alternative to String-based package names.
         */
        String[] basePackages() default {};
        
        /**
         * Configures the name of the {@link EntityManagerFactory} bean definition to be used to create repositories
         * discovered through this annotation. Defaults to {@code entityManagerFactory}.
         *
         * @return
         */
        String entityManagerFactoryRef() default "entityManagerFactory";
        
        /**
         * Configures the name of the {@link PlatformTransactionManager} bean definition to be used to create repositories
         * discovered through this annotation. Defaults to {@code transactionManager}.
         *
         * @return
         */
        String transactionManagerRef() default "transactionManager";
    }
    这里仅列了一些我们关心的方法。
    从源码我中我们可以看见,
    • basePackages: 使用此字段设置Repository的基本包,必须指向软件包中repository所在目录。
    • entityManagerFactoryRef:使用此字段引用默认或自定义的Entity Manager Factory, 这里通过Bean的名称进行指定, 默认Bean为entityManagerFactory。
    • transactionManagerRef:使用此字段引用默认或自定义的事务管理器,这里通过Bean的名称进行指定,默认Bean为transactionManager。
    通过上面的内容,我们为两个不同的数据源分别定义了不同的名称,所以我们需要在这里分别将其注入容器中。
    Primary

    @Configuration
    @EnableTransactionManagement
    @EnableJpaRepositories(basePackages = {"com.example.demo.repository.mysql"},
            entityManagerFactoryRef = "mysqlEntityManagerFactory", transactionManagerRef = "mysqlTransactionManager")
    public class MysqlDataSourceConfiguration {
        ...
    }
    secondary

    @Configuration
    @EnableTransactionManagement
    @EnableJpaRepositories(basePackages = "com.example.demo.repository.oracle",
            entityManagerFactoryRef = "oracleEntityManagerFactory", transactionManagerRef = "oracleTransactionManager")
    public class OracleDataSourceConfiguration {
        ...
    }

    完整的配置文件

    primary

    @Configuration
    @EnableTransactionManagement
    @EnableJpaRepositories(basePackages = {"com.example.demo.repository.mysql"},
            entityManagerFactoryRef = "mysqlEntityManagerFactory", transactionManagerRef = "mysqlTransactionManager")
    public class MysqlDataSourceConfiguration {
    
        @Primary
        @Bean(name = "mysqlDataSourceProperties")
        @ConfigurationProperties("spring.datasource")
        public DataSourceProperties dataSourceProperties() {
            return new DataSourceProperties();
        }
    
        @Primary
        @Bean(name = "mysqlDataSource")
        @ConfigurationProperties("spring.datasource.configuration")
        public DataSource dataSource (@Qualifier("mysqlDataSourceProperties") DataSourceProperties mysqlDataSourceProperties) {
            return mysqlDataSourceProperties.initializeDataSourceBuilder()
                    .type(HikariDataSource.class)
                    .build();
        }
    
        @Primary
        @Bean(name = "mysqlEntityManagerFactory")
        public LocalContainerEntityManagerFactoryBean entityManagerFactory(
                EntityManagerFactoryBuilder builder, @Qualifier("mysqlDataSource") DataSource mysqlDataSource) {
            return builder.dataSource(mysqlDataSource)
                    .packages("com.example.demo.model.mysql")
                    .persistenceUnit("mysql")
                    .build();
        }
    
        @Primary
        @Bean(name = "mysqlTransactionManager")
        public PlatformTransactionManager transactionManager(final @Qualifier("mysqlEntityManagerFactory")
                                                                         LocalContainerEntityManagerFactoryBean mysqlEntityManagerFactory) {
            return new JpaTransactionManager(mysqlEntityManagerFactory.getObject());
        }
    }
    secondary

    @Configuration
    @EnableTransactionManagement
    @EnableJpaRepositories(basePackages = "com.example.demo.repository.oracle",
            entityManagerFactoryRef = "oracleEntityManagerFactory", transactionManagerRef = "oracleTransactionManager")
    public class OracleDataSourceConfiguration {
        @Bean(name = "oracleDataSourceProperties")
        @ConfigurationProperties("spring.second.datasource")
        public DataSourceProperties dataSourceProperties() {
            return new DataSourceProperties();
        }
    
        @Bean
        @ConfigurationProperties("spring.second.datasource.configuration")
        public DataSource oracleDataSource(@Qualifier("oracleDataSourceProperties") DataSourceProperties oracleDataSourceProperties) {
            return oracleDataSourceProperties.initializeDataSourceBuilder()
                    .type(HikariDataSource.class)
                    .build();
        }
    
        @Bean(name = "oracleEntityManagerFactory")
        public LocalContainerEntityManagerFactoryBean oracleEntityManagerFactory(
                EntityManagerFactoryBuilder builder, @Qualifier("oracleDataSource") DataSource oracleDataSource) {
            return builder.dataSource(oracleDataSource)
                    .packages("com.example.demo.model.oracle")
                    .persistenceUnit("oracle")
                    .build();
        }
    
        @Bean
        public PlatformTransactionManager oracleTransactionManager(
                final @Qualifier("oracleEntityManagerFactory")
                        LocalContainerEntityManagerFactoryBean oracleEntityManagerFactory) {
            return new JpaTransactionManager(oracleEntityManagerFactory.getObject());
        }
    }

    总结

    当仅有一个数据源时,Spring Boot会默认自动配置好,但是如果使用多个数据源时,需要进行一些自定义的配置,以上便是全部的配置。
    总体感觉并不是特别复杂,耐心理解下,还是很容易理解的。
    如有错漏之处,还望各位大佬们指正。
     
     作者:吴家二少
    本文欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接 

  • 相关阅读:
    自学Python5.2-类和对象概念
    自学Python5.1-面向对象与面向过程
    自学Python2.1-基本数据类型-字符串str(object) 上
    自学Python2.10-跳出循环(break、continue)
    自学Python2.9-循环(while、for)
    自学Python2.8-条件(if、if...else)
    自学Python1.8-python input/print用法 格式化输出
    自学Python1.6-Centos内英文语法切换
    自学Python1.7-python变量以及类型
    自学Python1.5-Centos内python2识别中文
  • 原文地址:https://www.cnblogs.com/cloudman-open/p/13368977.html
Copyright © 2011-2022 走看看