出于业务需求,有时我们需要在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=
spring.ds_mysql.driverClassName=com.mysql.jdbc.Driver ## jdbc-second
spring.second.datasource.url=jdbc:oracle:thin:@localhost:/xxx.xxx.com
spring.second.datasource.userName=springboot
spring.second.datasource.password=
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=
spring.jpa.properties.hibernate.jdbc.batch_size=

数据源配置

需要注意的是,在配置多个数据源期间,必须将其中一个数据源标记为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会默认自动配置好,但是如果使用多个数据源时,需要进行一些自定义的配置,以上便是全部的配置。
总体感觉并不是特别复杂,耐心理解下,还是很容易理解的。
如有错漏之处,还望各位大佬们指正。
 

 作者:吴家二少
本文欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接

SpringBoot多数据库连接(mysql+oracle)的更多相关文章

  1. springboot+mybatis集成多数据源MySQL/Oracle/SqlServer

    日常开发中可能时常会遇到一些这样的需求,业务数据库和第三方数据库,两个或多个数据库属于不同数据库厂商,这时候就需要通过配置来实现对数据库实现多源处理.大致说一下我的业务场景,框架本身是配置的sprin ...

  2. springboot+mybatis+druid+sqlite/mysql/oracle

    搭建springboot+mybatis+druid+sqlite/mysql/oracle附带测试 1.版本 springboot2.1.6 jdk1.8 2.最简springboot环境 http ...

  3. Java学习-006-三种数据库连接 MySQL、Oracle、sqlserver

    此文主要讲述在初学 Java 时,常用的三种数据库 MySQL.Oracle.sqlserver 连接的源代码整理.希望能对初学 Java 编程的亲们有所帮助.若有不足之处,敬请大神指正,不胜感激!源 ...

  4. C#操作SqlServer MySql Oracle通用帮助类Db_Helper_DG(默认支持数据库读写分离、查询结果实体映射ORM)

    [前言] 作为一款成熟的面向对象高级编程语言,C#在ADO.Net的支持上已然是做的很成熟,我们可以方便地调用ADO.Net操作各类关系型数据库,在使用了多年的Sql_Helper_DG后,由于项目需 ...

  5. Java逆向工程SpringBoot + Mybatis Generator + MySQL

    Java逆向工程SpringBoot+ Mybatis Generator + MySQL Meven pop.xml文件添加引用: <dependency> <groupId> ...

  6. SQL Server,MySQL,Oracle三者的区别

    SQL Server,MySQL,Oracle三者的区别 2016-10-14 转自:SQL Server,MySQL,Oracle三者的区别 目录 1 Oracle.Sql Server.MySql ...

  7. Java与SQL Server, MySql, Oracle, Access的连接方法以及一些异常解决

    Java与SQL Server, MySql, Oracle, Access的连接方法以及一些异常解决 I. 概述 1.1 JDBC概念 JDBC(Java Database Connectivity ...

  8. C#操作SqlServer MySql Oracle通用帮助类

    C#操作SqlServer MySql Oracle通用帮助类 [前言] 作为一款成熟的面向对象高级编程语言,C#在ADO.Net的支持上已然是做的很成熟,我们可以方便地调用ADO.Net操作各类关系 ...

  9. spring + mybatis + mysql/oracle开发

    1)创建一个spring-mybatis-mysql这么一个javaweb或java工程 2)导入spring-ioc,spring-aop,spring-transaction,mybatis,c3 ...

  10. sql server 导出的datetime结果 CAST(0x00009E0E0095524F AS DateTime) 如何向mysql,oracle等数据库进行转换

    1. 处理 sql server 导出的 datetime 类型的字段 在进行sql server向mysql等其他数据进行迁移数据时,会发现使用sql server导出的datetime类型的结果是 ...

随机推荐

  1. 前后端分层架构MVC&MVVM

    早期 特点 页面由 JSP.PHP 等工程师在服务端生成 JSP 里揉杂大量业务代码 浏览器负责展现,服务端给什么就展现什么,展现的控制在 Web Server 层 优点 简单明快,本地起一个 Tom ...

  2. Jmeter系列(34)- 详解 Counter 计数器

    如果你想从头学习Jmeter,可以看看这个系列的文章哦 https://www.cnblogs.com/poloyy/category/1746599.html 简单介绍 计数器的作用:循环递增生成数 ...

  3. shiro之redis频繁访问问题

    目前安全框架shiro使用较为广泛,其功能也比较强大.为了分布式session共享,通常的做法是将session存储在redis中,实现多个节点获取同一个session.此实现可以实现session共 ...

  4. JavaScript基础避免使用eval()(006)

    许多人认为eval()方法是邪恶(evil)的.这个方法可以把任意字符串当成Javascript代码来执行.我们应该尽可能的不用eval()方法.比如,可以使用方括号来得到对象属性的值: // ant ...

  5. vue与react对比总结(一)

    一.react和vue设计上的共同理念 1.使用 Virtual DOM 2.提供了响应式 (Reactive) 和组件化 (Composable) 的视图组件. 3.将注意力集中保持在核心库,而将其 ...

  6. Halcon斑点分析涉及算子及其高阶运用

    涉及算子 获取图像 使用ROI 对齐ROI或图像 校正图像 基础内容这里不再重述 预处理图像(过滤) 基础: mean_image(平均平滑过滤),gauss_filter(高斯滤波),binomia ...

  7. java简介&&变量

    Day01 简介 数据 1.数据大体分为两类:基本类型和引用类型 2.基本类型的数据分为四类八种,四类为整型,浮点,布尔,字符 3.Long类型的数据超过int范围要在之后面加个L,不加L是整型会进行 ...

  8. MySQL 百万级数据量分页查询方法及其优化

    方法1: 直接使用数据库提供的SQL语句 语句样式: MySQL中,可用如下方法: SELECT * FROM 表名称 LIMIT M,N 适应场景: 适用于数据量较少的情况(元组百/千级) 原因/缺 ...

  9. spring boot 整合Thymeleaf模板

    SpringBoot 是为了简化 Spring 应用的创建.运行.调试.部署等一系列问题而诞生的产物,自动装配的特性让我们可以更好的关注业务本身而不是外部的XML配置,我们只需遵循规范,引入相关的依赖 ...

  10. python编程从入门到实践笔记

    我的第一个hello world 程序 print("hello python world") print("hello python world"*3) 打印 ...