quarkus数据库篇之三:单应用同时操作多个数据库
欢迎访问我的GitHub
这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos
本篇概览
- 一个应用同时连接多个数据库进行操作,这是常见的场景,quarkus也不例外,今天就随本文一起来实战多数据源操作
- 如下图,今天要创建名为multi-db-demo的应用,此应用同时连接两个数据库,名为fist-db的库中是卖家表,名为second-db的库中是买家表

- 为了简化demo,本篇继续坚持不支持web服务,用单元测试来验证应用同时操作两个数据库没有问题
限制
- quarkus连接和操作数据库的方式有两种:传统JDBC和反应式(reactive),咱们前文演示的demo就是传统JDBC方式
- 截止当前(最新版本是2.9),只有JDBC方式支持多数据源,反应式还不支持
准备工作
- 实战前先把环境准备一下,既然是多数据源操作,那就要准备至少两个数据库了,请您将MySQL和PostgreSQL准备好再做下面的数据准备工作
- 先在MySQL数据库建库建表,参考SQL如下
# 建数据库
CREATE DATABASE first_db;
# 选中数据库
use first_db;
# 建表
CREATE TABLE IF NOT EXISTS `seller`(
`id` INT UNSIGNED AUTO_INCREMENT,
`name` VARCHAR(100) NOT NULL,
`product_num` INT NULL,
PRIMARY KEY ( `id` )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
# 新增三条记录
insert into seller (name, product_num) values ('seller1', 1111);
insert into seller (name, product_num) values ('seller2', 2222);
insert into seller (name, product_num) values ('seller3', 3333);
- 然后是在PostgreSQL建库建表,参考SQL如下
# 建数据库
CREATE DATABASE second_db;
# 建表
CREATE TABLE buyer(
id SERIAL PRIMARY KEY,
name VARCHAR NOT NULL,
order_num int NOT NULL
);
# 新增两条记录
insert into buyer (name, order_num) values ('buyer1', 100);
insert into buyer (name, order_num) values ('buyer2', 200);
- 再整理一下两个数据库的地址,稍后用到
- MySQL:jdbc:mysql://192.168.50.43:3306/first_db
- PostgreSQL:jdbc:postgresql://192.168.50.43:15432/second_db
开发-创建子工程
- 《quarkus实战之一:准备工作》已创建了父工程,今天在此父工程下新增名为multi-db-demo的子工程,其pom与前文的工程区别不大,新增MySQL库,所有依赖如下
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<!-- JDBC库 -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-agroal</artifactId>
</dependency>
<!-- hibernate库 -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm</artifactId>
</dependency>
<!-- postgresql库 -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
<!-- mysql库 -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-mysql</artifactId>
</dependency>
<!-- 单元测试库 -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
开发-配置文件
- 接下来就是多数据源操作最关键的地方了:配置文件,为了满足多个profile的需要,这里继续使用application.properties和application-xxx.properties组合的方式,application.properties里存放公共配置,例如数据库类型,而application-xxx.properties里面是和各个profile环境有关的配置项,例如数据库IP地址、账号密码等,如下图

- 这里再强调一下配置的内容:配置的是数据源(datasource),代码中连接数据库时用到的配置项
- 接下来就是配置项了,这里有两个数据源,所以这两个数据源配置项都要有,咱们逐个配置
- 首先是first-db的,我们将其当做应用的默认数据源,那么它的配置和原来单数据源的没有任何却别,如下所示
# first-db的配置,下面五个配置项在application.properties文件中
quarkus.hibernate-orm.log.sql=true
quarkus.datasource.db-kind=mysql
quarkus.datasource.jdbc.max-size=8
quarkus.datasource.jdbc.min-size=2
quarkus.hibernate-orm.packages=com.bolingcavalry.multidb.entity.firstdb
# first-db的配置,下面三个配置项在application-test.properties文件中,即test环境下fitst-db的数据库地址、账号、密码等信息
quarkus.datasource.username=root
quarkus.datasource.password=123456
quarkus.datasource.jdbc.url=jdbc:mysql://192.168.50.43:3306/first_db
- 其次是second_db的配置,注意quarkus对非默认数据源配置的要求:配置项的key中都要有数据源名称,下图是默认数据源和非默认数据源配置项的对比,红色内容是数据源名称,放在第二个点号后面

- 按照上述规则,second_db的所有配置如下
# second_db的配置,下面五个配置项在application.properties文件中
quarkus.hibernate-orm.second_db.log.sql=true
quarkus.datasource.second_db.db-kind=postgresql
quarkus.datasource.second_db.jdbc.max-size=8
quarkus.datasource.second_db.jdbc.min-size=2
quarkus.hibernate-orm.second_db.datasource=second_db
quarkus.hibernate-orm.second_db.packages=com.bolingcavalry.multidb.entity.seconddb
# second_db的配置,下面三个配置项在application-test.properties文件中,即test环境下second_db的数据库地址、账号、密码等信息
quarkus.datasource.second_db.username=quarkus
quarkus.datasource.second_db.password=123456
quarkus.datasource.second_db.jdbc.url=jdbc:postgresql://192.168.50.43:15432/second_db
还要注意一点:quarkus.hibernate-orm.packages和quarkus.hibernate-orm.second_db.packages分别代表默认数据源和second_db各自表的entity类所在package,稍后编码写entity类的时候,seller表的entity只能放在com.bolingcavalry.multidb.entity.firstdb,buyer表的entity类只能放在com.bolingcavalry.multidb.entity.seconddb
配置完成,可以开始写代码了
开发-编码
先写entity类,注意entity类的package要对应quarkus.hibernate-orm.packages或者quarkus.hibernate-orm.second_db.packages这两个配置项的值
首先是first_db的卖家表seller的entity类,完整源码如下,注意主键生成的注解GeneratedValue的配置
package com.bolingcavalry.multidb.entity.firstdb;
import javax.persistence.*;
@Entity
@Table(name = "seller")
@NamedQuery(name = "Seller.findAll", query = "SELECT f FROM Seller f ORDER BY f.name", hints = @QueryHint(name = "org.hibernate.cacheable", value = "true"))
@Cacheable
public class Seller {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column
private String name;
@Column(name = "product_num")
private int productNum;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getProductNum() {
return productNum;
}
public void setProductNum(int productNum) {
this.productNum = productNum;
}
}
- 首先是second_db的买家表buyer的entity类,完整源码如下,注意主键生成的注解GeneratedValue的配置
package com.bolingcavalry.multidb.entity.seconddb;
import javax.persistence.*;
@Entity
@Table(name = "buyer")
@NamedQuery(name = "Buyer.findAll", query = "SELECT f FROM Buyer f ORDER BY f.name", hints = @QueryHint(name = "org.hibernate.cacheable", value = "true"))
@Cacheable
public class Buyer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column
private String name;
@Column(name = "order_num")
private int orderNum;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getOrderNum() {
return orderNum;
}
public void setOrderNum(int orderNum) {
this.orderNum = orderNum;
}
}
可见除了package要和配置项的指定值对齐,上述两个entity类并无任何特殊之处,不论单数据源还是多数据源,都是同样的写法
接下来是服务类了,先看卖家表对应的服务类SellerService.java,如下,由于seller表对应的数据库是当前应用的默认数据库,所以在操作数据库的时候,无需任何与数据源有关的特别设置,这和单数据源的应用是一样的
@ApplicationScoped
public class SellerService {
@Inject
EntityManager entityManager;
public List<Seller> get() {
return entityManager.createNamedQuery("Seller.findAll", Seller.class)
.getResultList();
}
public Seller getSingle(Integer id) {
return entityManager.find(Seller.class, id);
}
@Transactional
public void create(Seller seller) {
entityManager.persist(seller);
}
@Transactional
public void update(Integer id, Seller seller) {
Seller entity = entityManager.find(Seller.class, id);
if (null!=entity) {
entity.setName(seller.getName());
}
}
@Transactional
public void delete(Integer id) {
Seller entity = entityManager.getReference(Seller.class, id);
if (null!=entity) {
entityManager.remove(entity);
}
}
}
- 然后是买家表buyer相关操作的服务类BuyerService.java,可见它的成员变量entityManager多了个注解PersistenceUnit,值等于配置文件中的数据库名second_db,这个注解确保了entityManager用的是second_db的数据源,其他代码和单数据源的操作并无区别
package com.bolingcavalry.multidb.service;
import com.bolingcavalry.multidb.entity.seconddb.Buyer;
import io.quarkus.hibernate.orm.PersistenceUnit;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.transaction.Transactional;
import java.util.List;
@ApplicationScoped
public class BuyerService {
@Inject
@PersistenceUnit("second_db")
EntityManager entityManager;
public List<Buyer> get() {
return entityManager.createNamedQuery("Buyer.findAll", Buyer.class)
.getResultList();
}
public Buyer getSingle(Integer id) {
return entityManager.find(Buyer.class, id);
}
@Transactional
public void create(Buyer buyer) {
entityManager.persist(buyer);
}
@Transactional
public void update(Integer id, Buyer buyer) {
Buyer entity = entityManager.find(Buyer.class, id);
if (null!=entity) {
entity.setName(buyer.getName());
}
}
@Transactional
public void delete(Integer id) {
Buyer entity = entityManager.getReference(Buyer.class, id);
if (null!=entity) {
entityManager.remove(entity);
}
}
}
有个要格外注意的地方:PersistenceUnit类的package是io.quarkus.hibernate.orm,在import的时候要注意
代码写完了,接下来进入验证环节,依然使用单元测试来验证
开发-单元测试
- 虽然有两个服务类(SellerService和BuyerService),但是单元测试类只有一个,这里是为了模拟实际应用中同时操作两个数据库的场景,您也可以根据自身情况改成每个服务类一个单元测试类
@QuarkusTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class MultiDBTest {
/**
* first_db的seller表中,初始记录数
*/
private static final int FIRST_DB_EXIST_RECORDS_SIZE = 3;
/**
* second_db的buyer表中,初始记录数
*/
private static final int SECOND_DB_EXIST_RECORDS_SIZE = 2;
/**
* import.sql中,第一条记录的id
*/
private static final int EXIST_FIRST_ID = 1;
/**
* 在Fruit.java中,id字段的SequenceGenerator指定了initialValue等于10,
* 表示自增ID从10开始
*/
private static final int ID_SEQUENCE_INIT_VALUE = 10;
@Inject
SellerService sellerService;
@Inject
BuyerService buyerService;
@Test
@DisplayName("list")
@Order(1)
public void testGet() {
List<Seller> sellerList = sellerService.get();
// 判定非空
Assertions.assertNotNull(sellerList);
// seller表初始化时新增了3条记录
Assertions.assertEquals(FIRST_DB_EXIST_RECORDS_SIZE, sellerList.size());
List<Buyer> buyerList = buyerService.get();
// 判定非空
Assertions.assertNotNull(buyerList);
// buyer表初始化时新增了2条记录
Assertions.assertEquals(SECOND_DB_EXIST_RECORDS_SIZE, buyerList.size());
}
@Test
@DisplayName("getSingle")
@Order(2)
public void testGetSingle() {
// 用第二条记录吧,第一条在执行testUpdate方法时被更改了
Seller seller = sellerService.getSingle(EXIST_FIRST_ID+1);
// 判定非空
Assertions.assertNotNull(seller);
// buyer表的第一条记录
Assertions.assertEquals("seller2", seller.getName());
// 用第二条记录吧,第一条在执行testUpdate方法时被更改了
Buyer buyer = buyerService.getSingle(EXIST_FIRST_ID+1);
// 判定非空
Assertions.assertNotNull(buyer);
// buyer表的第二条记录
Assertions.assertEquals("buyer2", buyer.getName());
}
@Test
@DisplayName("update")
@Order(3)
public void testUpdate() {
// 验证first_db的操作
String newName = LocalDateTime.now().toString();
Seller seller = new Seller();
seller.setName(newName);
// 更新数据库
sellerService.update(EXIST_FIRST_ID, seller);
Seller sellerFromDB = sellerService.getSingle(EXIST_FIRST_ID);
// 从数据库取出的对象,其名称应该等于修改的名称
Assertions.assertEquals(newName, sellerFromDB.getName());
// 验证second_db的操作
Buyer buyer = new Buyer();
buyer.setName(newName);
// 更新数据库
buyerService.update(EXIST_FIRST_ID, buyer);
Buyer buyerFromDB = buyerService.getSingle(EXIST_FIRST_ID);
// 从数据库取出的对象,其名称应该等于修改的名称
Assertions.assertEquals(newName, buyerFromDB.getName());
}
@Test
@DisplayName("create")
@Order(3)
public void testCreate() {
Seller seller = new Seller();
seller.setName("seller4");
sellerService.create(seller);
// 创建成功后,记录主键肯定是大于3的
Assertions.assertTrue(seller.getId()>FIRST_DB_EXIST_RECORDS_SIZE);
// 记录总数应该等于已有记录数+1
Assertions.assertEquals(FIRST_DB_EXIST_RECORDS_SIZE+1, sellerService.get().size());
Buyer buyer = new Buyer();
buyer.setName("buyer3");
buyerService.create(buyer);
// 创建成功后,记录主键肯定是大于3的
Assertions.assertTrue(buyer.getId()>SECOND_DB_EXIST_RECORDS_SIZE);
// 记录总数应该等于已有记录数+1
Assertions.assertEquals(SECOND_DB_EXIST_RECORDS_SIZE+1, buyerService.get().size());
}
@Test
@DisplayName("delete")
@Order(5)
public void testDelete() {
List<Seller> sellers = sellerService.get();
// 先记删除前的总数
int numBeforeDelete = sellers.size();
// 删除最后一条记录
sellerService.delete(sellers.get(numBeforeDelete-1).getId());
// 记录数应该应该等于删除前的数量减一
Assertions.assertEquals(numBeforeDelete-1, sellerService.get().size());
List<Buyer> buyers = buyerService.get();
// 先记删除前的总数
numBeforeDelete = buyers.size();
// 删除最后一条记录
buyerService.delete(buyers.get(numBeforeDelete-1).getId());
// 记录数应该应该等于删除前的数量减一
Assertions.assertEquals(numBeforeDelete-1, buyerService.get().size());
}
}
- 代码中已经有详细的注释,就不多赘述了
验证
- 请再次确认数据库、表、记录都已经准备就绪
- 运行单元测试类,如下图,一切符合预期

- 去数据库看一下,如下图红框所示,那是执行testUpdate方法时更新的结果

- 至此,quarkus连接多个数据库的实战操作已完成,希望这个实用技能可以给您一些参考
源码下载
- 本篇实战的完整源码可在GitHub下载到,地址和链接信息如下表所示(https://github.com/zq2599/blog_demos)
| 名称 | 链接 | 备注 |
|---|---|---|
| 项目主页 | https://github.com/zq2599/blog_demos | 该项目在GitHub上的主页 |
| git仓库地址(https) | https://github.com/zq2599/blog_demos.git | 该项目源码的仓库地址,https协议 |
| git仓库地址(ssh) | git@github.com:zq2599/blog_demos.git | 该项目源码的仓库地址,ssh协议 |
- 这个git项目中有多个文件夹,本次实战的源码在quarkus-tutorials文件夹下,如下图红框

- quarkus-tutorials是个父工程,里面有多个module,本篇实战的module是multi-db-demo,如下图红框

本篇概览
- 一个应用同时连接多个数据库进行操作,这是常见的场景,quarkus也不例外,今天就随本文一起来实战多数据源操作
- 如下图,今天要创建名为multi-db-demo的应用,此应用同时连接两个数据库,名为fist-db的库中是卖家表,名为second-db的库中是买家表

- 为了简化demo,本篇继续坚持不支持web服务,用单元测试来验证应用同时操作两个数据库没有问题
限制
- quarkus连接和操作数据库的方式有两种:传统JDBC和反应式(reactive),咱们前文演示的demo就是传统JDBC方式
- 截止当前(最新版本是2.9),只有JDBC方式支持多数据源,反应式还不支持
准备工作
- 实战前先把环境准备一下,既然是多数据源操作,那就要准备至少两个数据库了,请您将MySQL和PostgreSQL准备好再做下面的数据准备工作
- 先在MySQL数据库建库建表,参考SQL如下
# 建数据库
CREATE DATABASE first_db;
# 选中数据库
use first_db;
# 建表
CREATE TABLE IF NOT EXISTS `seller`(
`id` INT UNSIGNED AUTO_INCREMENT,
`name` VARCHAR(100) NOT NULL,
`product_num` INT NULL,
PRIMARY KEY ( `id` )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
# 新增三条记录
insert into seller (name, product_num) values ('seller1', 1111);
insert into seller (name, product_num) values ('seller2', 2222);
insert into seller (name, product_num) values ('seller3', 3333);
- 然后是在PostgreSQL建库建表,参考SQL如下
# 建数据库
CREATE DATABASE second_db;
# 建表
CREATE TABLE buyer(
id SERIAL PRIMARY KEY,
name VARCHAR NOT NULL,
order_num int NOT NULL
);
# 新增两条记录
insert into buyer (name, order_num) values ('buyer1', 100);
insert into buyer (name, order_num) values ('buyer2', 200);
- 再整理一下两个数据库的地址,稍后用到
- MySQL:jdbc:mysql://192.168.50.43:3306/first_db
- PostgreSQL:jdbc:postgresql://192.168.50.43:15432/second_db
开发-创建子工程
- 《quarkus实战之一:准备工作》已创建了父工程,今天在此父工程下新增名为multi-db-demo的子工程,其pom与前文的工程区别不大,新增MySQL库,所有依赖如下
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<!-- JDBC库 -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-agroal</artifactId>
</dependency>
<!-- hibernate库 -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm</artifactId>
</dependency>
<!-- postgresql库 -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
<!-- mysql库 -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-mysql</artifactId>
</dependency>
<!-- 单元测试库 -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
开发-配置文件
- 接下来就是多数据源操作最关键的地方了:配置文件,为了满足多个profile的需要,这里继续使用application.properties和application-xxx.properties组合的方式,application.properties里存放公共配置,例如数据库类型,而application-xxx.properties里面是和各个profile环境有关的配置项,例如数据库IP地址、账号密码等,如下图

- 这里再强调一下配置的内容:配置的是数据源(datasource),代码中连接数据库时用到的配置项
- 接下来就是配置项了,这里有两个数据源,所以这两个数据源配置项都要有,咱们逐个配置
- 首先是first-db的,我们将其当做应用的默认数据源,那么它的配置和原来单数据源的没有任何却别,如下所示
# first-db的配置,下面五个配置项在application.properties文件中
quarkus.hibernate-orm.log.sql=true
quarkus.datasource.db-kind=mysql
quarkus.datasource.jdbc.max-size=8
quarkus.datasource.jdbc.min-size=2
quarkus.hibernate-orm.packages=com.bolingcavalry.multidb.entity.firstdb
# first-db的配置,下面三个配置项在application-test.properties文件中,即test环境下fitst-db的数据库地址、账号、密码等信息
quarkus.datasource.username=root
quarkus.datasource.password=123456
quarkus.datasource.jdbc.url=jdbc:mysql://192.168.50.43:3306/first_db
- 其次是second_db的配置,注意quarkus对非默认数据源配置的要求:配置项的key中都要有数据源名称,下图是默认数据源和非默认数据源配置项的对比,红色内容是数据源名称,放在第二个点号后面

- 按照上述规则,second_db的所有配置如下
# second_db的配置,下面五个配置项在application.properties文件中
quarkus.hibernate-orm.second_db.log.sql=true
quarkus.datasource.second_db.db-kind=postgresql
quarkus.datasource.second_db.jdbc.max-size=8
quarkus.datasource.second_db.jdbc.min-size=2
quarkus.hibernate-orm.second_db.datasource=second_db
quarkus.hibernate-orm.second_db.packages=com.bolingcavalry.multidb.entity.seconddb
# second_db的配置,下面三个配置项在application-test.properties文件中,即test环境下second_db的数据库地址、账号、密码等信息
quarkus.datasource.second_db.username=quarkus
quarkus.datasource.second_db.password=123456
quarkus.datasource.second_db.jdbc.url=jdbc:postgresql://192.168.50.43:15432/second_db
还要注意一点:quarkus.hibernate-orm.packages和quarkus.hibernate-orm.second_db.packages分别代表默认数据源和second_db各自表的entity类所在package,稍后编码写entity类的时候,seller表的entity只能放在com.bolingcavalry.multidb.entity.firstdb,buyer表的entity类只能放在com.bolingcavalry.multidb.entity.seconddb
配置完成,可以开始写代码了
开发:编码
先写entity类,注意entity类的package要对应quarkus.hibernate-orm.packages或者quarkus.hibernate-orm.second_db.packages这两个配置项的值
首先是first_db的卖家表seller的entity类,完整源码如下,注意主键生成的注解GeneratedValue的配置
package com.bolingcavalry.multidb.entity.firstdb;
import javax.persistence.*;
@Entity
@Table(name = "seller")
@NamedQuery(name = "Seller.findAll", query = "SELECT f FROM Seller f ORDER BY f.name", hints = @QueryHint(name = "org.hibernate.cacheable", value = "true"))
@Cacheable
public class Seller {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column
private String name;
@Column(name = "product_num")
private int productNum;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getProductNum() {
return productNum;
}
public void setProductNum(int productNum) {
this.productNum = productNum;
}
}
- 首先是second_db的买家表buyer的entity类,完整源码如下,注意主键生成的注解GeneratedValue的配置
package com.bolingcavalry.multidb.entity.seconddb;
import javax.persistence.*;
@Entity
@Table(name = "buyer")
@NamedQuery(name = "Buyer.findAll", query = "SELECT f FROM Buyer f ORDER BY f.name", hints = @QueryHint(name = "org.hibernate.cacheable", value = "true"))
@Cacheable
public class Buyer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column
private String name;
@Column(name = "order_num")
private int orderNum;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getOrderNum() {
return orderNum;
}
public void setOrderNum(int orderNum) {
this.orderNum = orderNum;
}
}
可见除了package要和配置项的指定值对齐,上述两个entity类并无任何特殊之处,不论单数据源还是多数据源,都是同样的写法
接下来是服务类了,先看卖家表对应的服务类SellerService.java,如下,由于seller表对应的数据库是当前应用的默认数据库,所以在操作数据库的时候,无需任何与数据源有关的特别设置,这和单数据源的应用是一样的
@ApplicationScoped
public class SellerService {
@Inject
EntityManager entityManager;
public List<Seller> get() {
return entityManager.createNamedQuery("Seller.findAll", Seller.class)
.getResultList();
}
public Seller getSingle(Integer id) {
return entityManager.find(Seller.class, id);
}
@Transactional
public void create(Seller seller) {
entityManager.persist(seller);
}
@Transactional
public void update(Integer id, Seller seller) {
Seller entity = entityManager.find(Seller.class, id);
if (null!=entity) {
entity.setName(seller.getName());
}
}
@Transactional
public void delete(Integer id) {
Seller entity = entityManager.getReference(Seller.class, id);
if (null!=entity) {
entityManager.remove(entity);
}
}
}
- 然后是买家表buyer相关操作的服务类BuyerService.java,可见它的成员变量entityManager多了个注解PersistenceUnit,值等于配置文件中的数据库名second_db,这个注解确保了entityManager用的是second_db的数据源,其他代码和单数据源的操作并无区别
package com.bolingcavalry.multidb.service;
import com.bolingcavalry.multidb.entity.seconddb.Buyer;
import io.quarkus.hibernate.orm.PersistenceUnit;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.transaction.Transactional;
import java.util.List;
@ApplicationScoped
public class BuyerService {
@Inject
@PersistenceUnit("second_db")
EntityManager entityManager;
public List<Buyer> get() {
return entityManager.createNamedQuery("Buyer.findAll", Buyer.class)
.getResultList();
}
public Buyer getSingle(Integer id) {
return entityManager.find(Buyer.class, id);
}
@Transactional
public void create(Buyer buyer) {
entityManager.persist(buyer);
}
@Transactional
public void update(Integer id, Buyer buyer) {
Buyer entity = entityManager.find(Buyer.class, id);
if (null!=entity) {
entity.setName(buyer.getName());
}
}
@Transactional
public void delete(Integer id) {
Buyer entity = entityManager.getReference(Buyer.class, id);
if (null!=entity) {
entityManager.remove(entity);
}
}
}
有个要格外注意的地方:PersistenceUnit类的package是io.quarkus.hibernate.orm,在import的时候要注意
代码写完了,接下来进入验证环节,依然使用单元测试来验证
开发-单元测试
- 虽然有两个服务类(SellerService和BuyerService),但是单元测试类只有一个,这里是为了模拟实际应用中同时操作两个数据库的场景,您也可以根据自身情况改成每个服务类一个单元测试类
@QuarkusTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class MultiDBTest {
/**
* first_db的seller表中,初始记录数
*/
private static final int FIRST_DB_EXIST_RECORDS_SIZE = 3;
/**
* second_db的buyer表中,初始记录数
*/
private static final int SECOND_DB_EXIST_RECORDS_SIZE = 2;
/**
* import.sql中,第一条记录的id
*/
private static final int EXIST_FIRST_ID = 1;
/**
* 在Fruit.java中,id字段的SequenceGenerator指定了initialValue等于10,
* 表示自增ID从10开始
*/
private static final int ID_SEQUENCE_INIT_VALUE = 10;
@Inject
SellerService sellerService;
@Inject
BuyerService buyerService;
@Test
@DisplayName("list")
@Order(1)
public void testGet() {
List<Seller> sellerList = sellerService.get();
// 判定非空
Assertions.assertNotNull(sellerList);
// seller表初始化时新增了3条记录
Assertions.assertEquals(FIRST_DB_EXIST_RECORDS_SIZE, sellerList.size());
List<Buyer> buyerList = buyerService.get();
// 判定非空
Assertions.assertNotNull(buyerList);
// buyer表初始化时新增了2条记录
Assertions.assertEquals(SECOND_DB_EXIST_RECORDS_SIZE, buyerList.size());
}
@Test
@DisplayName("getSingle")
@Order(2)
public void testGetSingle() {
// 用第二条记录吧,第一条在执行testUpdate方法时被更改了
Seller seller = sellerService.getSingle(EXIST_FIRST_ID+1);
// 判定非空
Assertions.assertNotNull(seller);
// buyer表的第一条记录
Assertions.assertEquals("seller2", seller.getName());
// 用第二条记录吧,第一条在执行testUpdate方法时被更改了
Buyer buyer = buyerService.getSingle(EXIST_FIRST_ID+1);
// 判定非空
Assertions.assertNotNull(buyer);
// buyer表的第二条记录
Assertions.assertEquals("buyer2", buyer.getName());
}
@Test
@DisplayName("update")
@Order(3)
public void testUpdate() {
// 验证first_db的操作
String newName = LocalDateTime.now().toString();
Seller seller = new Seller();
seller.setName(newName);
// 更新数据库
sellerService.update(EXIST_FIRST_ID, seller);
Seller sellerFromDB = sellerService.getSingle(EXIST_FIRST_ID);
// 从数据库取出的对象,其名称应该等于修改的名称
Assertions.assertEquals(newName, sellerFromDB.getName());
// 验证second_db的操作
Buyer buyer = new Buyer();
buyer.setName(newName);
// 更新数据库
buyerService.update(EXIST_FIRST_ID, buyer);
Buyer buyerFromDB = buyerService.getSingle(EXIST_FIRST_ID);
// 从数据库取出的对象,其名称应该等于修改的名称
Assertions.assertEquals(newName, buyerFromDB.getName());
}
@Test
@DisplayName("create")
@Order(3)
public void testCreate() {
Seller seller = new Seller();
seller.setName("seller4");
sellerService.create(seller);
// 创建成功后,记录主键肯定是大于3的
Assertions.assertTrue(seller.getId()>FIRST_DB_EXIST_RECORDS_SIZE);
// 记录总数应该等于已有记录数+1
Assertions.assertEquals(FIRST_DB_EXIST_RECORDS_SIZE+1, sellerService.get().size());
Buyer buyer = new Buyer();
buyer.setName("buyer3");
buyerService.create(buyer);
// 创建成功后,记录主键肯定是大于3的
Assertions.assertTrue(buyer.getId()>SECOND_DB_EXIST_RECORDS_SIZE);
// 记录总数应该等于已有记录数+1
Assertions.assertEquals(SECOND_DB_EXIST_RECORDS_SIZE+1, buyerService.get().size());
}
@Test
@DisplayName("delete")
@Order(5)
public void testDelete() {
List<Seller> sellers = sellerService.get();
// 先记删除前的总数
int numBeforeDelete = sellers.size();
// 删除最后一条记录
sellerService.delete(sellers.get(numBeforeDelete-1).getId());
// 记录数应该应该等于删除前的数量减一
Assertions.assertEquals(numBeforeDelete-1, sellerService.get().size());
List<Buyer> buyers = buyerService.get();
// 先记删除前的总数
numBeforeDelete = buyers.size();
// 删除最后一条记录
buyerService.delete(buyers.get(numBeforeDelete-1).getId());
// 记录数应该应该等于删除前的数量减一
Assertions.assertEquals(numBeforeDelete-1, buyerService.get().size());
}
}
- 代码中已经有详细的注释,就不多赘述了
验证
- 请再次确认数据库、表、记录都已经准备就绪
- 运行单元测试类,如下图,一切符合预期

- 去数据库看一下,如下图红框所示,那是执行testUpdate方法时更新的结果

- 至此,quarkus连接多个数据库的实战操作已完成,希望这个实用技能可以给您一些参考
源码下载
- 本篇实战的完整源码可在GitHub下载到,地址和链接信息如下表所示(https://github.com/zq2599/blog_demos)
| 名称 | 链接 | 备注 |
|---|---|---|
| 项目主页 | https://github.com/zq2599/blog_demos | 该项目在GitHub上的主页 |
| git仓库地址(https) | https://github.com/zq2599/blog_demos.git | 该项目源码的仓库地址,https协议 |
| git仓库地址(ssh) | git@github.com:zq2599/blog_demos.git | 该项目源码的仓库地址,ssh协议 |
- 这个git项目中有多个文件夹,本次实战的源码在quarkus-tutorials文件夹下,如下图红框

- quarkus-tutorials是个父工程,里面有多个module,本篇实战的module是multi-db-demo,如下图红框

本篇概览
- 一个应用同时连接多个数据库进行操作,这是常见的场景,quarkus也不例外,今天就随本文一起来实战多数据源操作
- 如下图,今天要创建名为multi-db-demo的应用,此应用同时连接两个数据库,名为fist-db的库中是卖家表,名为second-db的库中是买家表

- 为了简化demo,本篇继续坚持不支持web服务,用单元测试来验证应用同时操作两个数据库没有问题
限制
- quarkus连接和操作数据库的方式有两种:传统JDBC和反应式(reactive),咱们前文演示的demo就是传统JDBC方式
- 截止当前(最新版本是2.9),只有JDBC方式支持多数据源,反应式还不支持
准备工作
- 实战前先把环境准备一下,既然是多数据源操作,那就要准备至少两个数据库了,请您将MySQL和PostgreSQL准备好再做下面的数据准备工作
- 先在MySQL数据库建库建表,参考SQL如下
# 建数据库
CREATE DATABASE first_db;
# 选中数据库
use first_db;
# 建表
CREATE TABLE IF NOT EXISTS `seller`(
`id` INT UNSIGNED AUTO_INCREMENT,
`name` VARCHAR(100) NOT NULL,
`product_num` INT NULL,
PRIMARY KEY ( `id` )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
# 新增三条记录
insert into seller (name, product_num) values ('seller1', 1111);
insert into seller (name, product_num) values ('seller2', 2222);
insert into seller (name, product_num) values ('seller3', 3333);
- 然后是在PostgreSQL建库建表,参考SQL如下
# 建数据库
CREATE DATABASE second_db;
# 建表
CREATE TABLE buyer(
id SERIAL PRIMARY KEY,
name VARCHAR NOT NULL,
order_num int NOT NULL
);
# 新增两条记录
insert into buyer (name, order_num) values ('buyer1', 100);
insert into buyer (name, order_num) values ('buyer2', 200);
- 再整理一下两个数据库的地址,稍后用到
- MySQL:jdbc:mysql://192.168.50.43:3306/first_db
- PostgreSQL:jdbc:postgresql://192.168.50.43:15432/second_db
开发-创建子工程
- 《quarkus实战之一:准备工作》已创建了父工程,今天在此父工程下新增名为multi-db-demo的子工程,其pom与前文的工程区别不大,新增MySQL库,所有依赖如下
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<!-- JDBC库 -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-agroal</artifactId>
</dependency>
<!-- hibernate库 -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm</artifactId>
</dependency>
<!-- postgresql库 -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
<!-- mysql库 -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-mysql</artifactId>
</dependency>
<!-- 单元测试库 -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
开发-配置文件
- 接下来就是多数据源操作最关键的地方了:配置文件,为了满足多个profile的需要,这里继续使用application.properties和application-xxx.properties组合的方式,application.properties里存放公共配置,例如数据库类型,而application-xxx.properties里面是和各个profile环境有关的配置项,例如数据库IP地址、账号密码等,如下图

- 这里再强调一下配置的内容:配置的是数据源(datasource),代码中连接数据库时用到的配置项
- 接下来就是配置项了,这里有两个数据源,所以这两个数据源配置项都要有,咱们逐个配置
- 首先是first-db的,我们将其当做应用的默认数据源,那么它的配置和原来单数据源的没有任何却别,如下所示
# first-db的配置,下面五个配置项在application.properties文件中
quarkus.hibernate-orm.log.sql=true
quarkus.datasource.db-kind=mysql
quarkus.datasource.jdbc.max-size=8
quarkus.datasource.jdbc.min-size=2
quarkus.hibernate-orm.packages=com.bolingcavalry.multidb.entity.firstdb
# first-db的配置,下面三个配置项在application-test.properties文件中,即test环境下fitst-db的数据库地址、账号、密码等信息
quarkus.datasource.username=root
quarkus.datasource.password=123456
quarkus.datasource.jdbc.url=jdbc:mysql://192.168.50.43:3306/first_db
- 其次是second_db的配置,注意quarkus对非默认数据源配置的要求:配置项的key中都要有数据源名称,下图是默认数据源和非默认数据源配置项的对比,红色内容是数据源名称,放在第二个点号后面

- 按照上述规则,second_db的所有配置如下
# second_db的配置,下面五个配置项在application.properties文件中
quarkus.hibernate-orm.second_db.log.sql=true
quarkus.datasource.second_db.db-kind=postgresql
quarkus.datasource.second_db.jdbc.max-size=8
quarkus.datasource.second_db.jdbc.min-size=2
quarkus.hibernate-orm.second_db.datasource=second_db
quarkus.hibernate-orm.second_db.packages=com.bolingcavalry.multidb.entity.seconddb
# second_db的配置,下面三个配置项在application-test.properties文件中,即test环境下second_db的数据库地址、账号、密码等信息
quarkus.datasource.second_db.username=quarkus
quarkus.datasource.second_db.password=123456
quarkus.datasource.second_db.jdbc.url=jdbc:postgresql://192.168.50.43:15432/second_db
还要注意一点:quarkus.hibernate-orm.packages和quarkus.hibernate-orm.second_db.packages分别代表默认数据源和second_db各自表的entity类所在package,稍后编码写entity类的时候,seller表的entity只能放在com.bolingcavalry.multidb.entity.firstdb,buyer表的entity类只能放在com.bolingcavalry.multidb.entity.seconddb
配置完成,可以开始写代码了
开发:编码
先写entity类,注意entity类的package要对应quarkus.hibernate-orm.packages或者quarkus.hibernate-orm.second_db.packages这两个配置项的值
首先是first_db的卖家表seller的entity类,完整源码如下,注意主键生成的注解GeneratedValue的配置
package com.bolingcavalry.multidb.entity.firstdb;
import javax.persistence.*;
@Entity
@Table(name = "seller")
@NamedQuery(name = "Seller.findAll", query = "SELECT f FROM Seller f ORDER BY f.name", hints = @QueryHint(name = "org.hibernate.cacheable", value = "true"))
@Cacheable
public class Seller {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column
private String name;
@Column(name = "product_num")
private int productNum;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getProductNum() {
return productNum;
}
public void setProductNum(int productNum) {
this.productNum = productNum;
}
}
- 首先是second_db的买家表buyer的entity类,完整源码如下,注意主键生成的注解GeneratedValue的配置
package com.bolingcavalry.multidb.entity.seconddb;
import javax.persistence.*;
@Entity
@Table(name = "buyer")
@NamedQuery(name = "Buyer.findAll", query = "SELECT f FROM Buyer f ORDER BY f.name", hints = @QueryHint(name = "org.hibernate.cacheable", value = "true"))
@Cacheable
public class Buyer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column
private String name;
@Column(name = "order_num")
private int orderNum;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getOrderNum() {
return orderNum;
}
public void setOrderNum(int orderNum) {
this.orderNum = orderNum;
}
}
可见除了package要和配置项的指定值对齐,上述两个entity类并无任何特殊之处,不论单数据源还是多数据源,都是同样的写法
接下来是服务类了,先看卖家表对应的服务类SellerService.java,如下,由于seller表对应的数据库是当前应用的默认数据库,所以在操作数据库的时候,无需任何与数据源有关的特别设置,这和单数据源的应用是一样的
@ApplicationScoped
public class SellerService {
@Inject
EntityManager entityManager;
public List<Seller> get() {
return entityManager.createNamedQuery("Seller.findAll", Seller.class)
.getResultList();
}
public Seller getSingle(Integer id) {
return entityManager.find(Seller.class, id);
}
@Transactional
public void create(Seller seller) {
entityManager.persist(seller);
}
@Transactional
public void update(Integer id, Seller seller) {
Seller entity = entityManager.find(Seller.class, id);
if (null!=entity) {
entity.setName(seller.getName());
}
}
@Transactional
public void delete(Integer id) {
Seller entity = entityManager.getReference(Seller.class, id);
if (null!=entity) {
entityManager.remove(entity);
}
}
}
- 然后是买家表buyer相关操作的服务类BuyerService.java,可见它的成员变量entityManager多了个注解PersistenceUnit,值等于配置文件中的数据库名second_db,这个注解确保了entityManager用的是second_db的数据源,其他代码和单数据源的操作并无区别
package com.bolingcavalry.multidb.service;
import com.bolingcavalry.multidb.entity.seconddb.Buyer;
import io.quarkus.hibernate.orm.PersistenceUnit;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.transaction.Transactional;
import java.util.List;
@ApplicationScoped
public class BuyerService {
@Inject
@PersistenceUnit("second_db")
EntityManager entityManager;
public List<Buyer> get() {
return entityManager.createNamedQuery("Buyer.findAll", Buyer.class)
.getResultList();
}
public Buyer getSingle(Integer id) {
return entityManager.find(Buyer.class, id);
}
@Transactional
public void create(Buyer buyer) {
entityManager.persist(buyer);
}
@Transactional
public void update(Integer id, Buyer buyer) {
Buyer entity = entityManager.find(Buyer.class, id);
if (null!=entity) {
entity.setName(buyer.getName());
}
}
@Transactional
public void delete(Integer id) {
Buyer entity = entityManager.getReference(Buyer.class, id);
if (null!=entity) {
entityManager.remove(entity);
}
}
}
有个要格外注意的地方:PersistenceUnit类的package是io.quarkus.hibernate.orm,在import的时候要注意
代码写完了,接下来进入验证环节,依然使用单元测试来验证
开发-单元测试
- 虽然有两个服务类(SellerService和BuyerService),但是单元测试类只有一个,这里是为了模拟实际应用中同时操作两个数据库的场景,您也可以根据自身情况改成每个服务类一个单元测试类
@QuarkusTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class MultiDBTest {
/**
* first_db的seller表中,初始记录数
*/
private static final int FIRST_DB_EXIST_RECORDS_SIZE = 3;
/**
* second_db的buyer表中,初始记录数
*/
private static final int SECOND_DB_EXIST_RECORDS_SIZE = 2;
/**
* import.sql中,第一条记录的id
*/
private static final int EXIST_FIRST_ID = 1;
/**
* 在Fruit.java中,id字段的SequenceGenerator指定了initialValue等于10,
* 表示自增ID从10开始
*/
private static final int ID_SEQUENCE_INIT_VALUE = 10;
@Inject
SellerService sellerService;
@Inject
BuyerService buyerService;
@Test
@DisplayName("list")
@Order(1)
public void testGet() {
List<Seller> sellerList = sellerService.get();
// 判定非空
Assertions.assertNotNull(sellerList);
// seller表初始化时新增了3条记录
Assertions.assertEquals(FIRST_DB_EXIST_RECORDS_SIZE, sellerList.size());
List<Buyer> buyerList = buyerService.get();
// 判定非空
Assertions.assertNotNull(buyerList);
// buyer表初始化时新增了2条记录
Assertions.assertEquals(SECOND_DB_EXIST_RECORDS_SIZE, buyerList.size());
}
@Test
@DisplayName("getSingle")
@Order(2)
public void testGetSingle() {
// 用第二条记录吧,第一条在执行testUpdate方法时被更改了
Seller seller = sellerService.getSingle(EXIST_FIRST_ID+1);
// 判定非空
Assertions.assertNotNull(seller);
// buyer表的第一条记录
Assertions.assertEquals("seller2", seller.getName());
// 用第二条记录吧,第一条在执行testUpdate方法时被更改了
Buyer buyer = buyerService.getSingle(EXIST_FIRST_ID+1);
// 判定非空
Assertions.assertNotNull(buyer);
// buyer表的第二条记录
Assertions.assertEquals("buyer2", buyer.getName());
}
@Test
@DisplayName("update")
@Order(3)
public void testUpdate() {
// 验证first_db的操作
String newName = LocalDateTime.now().toString();
Seller seller = new Seller();
seller.setName(newName);
// 更新数据库
sellerService.update(EXIST_FIRST_ID, seller);
Seller sellerFromDB = sellerService.getSingle(EXIST_FIRST_ID);
// 从数据库取出的对象,其名称应该等于修改的名称
Assertions.assertEquals(newName, sellerFromDB.getName());
// 验证second_db的操作
Buyer buyer = new Buyer();
buyer.setName(newName);
// 更新数据库
buyerService.update(EXIST_FIRST_ID, buyer);
Buyer buyerFromDB = buyerService.getSingle(EXIST_FIRST_ID);
// 从数据库取出的对象,其名称应该等于修改的名称
Assertions.assertEquals(newName, buyerFromDB.getName());
}
@Test
@DisplayName("create")
@Order(3)
public void testCreate() {
Seller seller = new Seller();
seller.setName("seller4");
sellerService.create(seller);
// 创建成功后,记录主键肯定是大于3的
Assertions.assertTrue(seller.getId()>FIRST_DB_EXIST_RECORDS_SIZE);
// 记录总数应该等于已有记录数+1
Assertions.assertEquals(FIRST_DB_EXIST_RECORDS_SIZE+1, sellerService.get().size());
Buyer buyer = new Buyer();
buyer.setName("buyer3");
buyerService.create(buyer);
// 创建成功后,记录主键肯定是大于3的
Assertions.assertTrue(buyer.getId()>SECOND_DB_EXIST_RECORDS_SIZE);
// 记录总数应该等于已有记录数+1
Assertions.assertEquals(SECOND_DB_EXIST_RECORDS_SIZE+1, buyerService.get().size());
}
@Test
@DisplayName("delete")
@Order(5)
public void testDelete() {
List<Seller> sellers = sellerService.get();
// 先记删除前的总数
int numBeforeDelete = sellers.size();
// 删除最后一条记录
sellerService.delete(sellers.get(numBeforeDelete-1).getId());
// 记录数应该应该等于删除前的数量减一
Assertions.assertEquals(numBeforeDelete-1, sellerService.get().size());
List<Buyer> buyers = buyerService.get();
// 先记删除前的总数
numBeforeDelete = buyers.size();
// 删除最后一条记录
buyerService.delete(buyers.get(numBeforeDelete-1).getId());
// 记录数应该应该等于删除前的数量减一
Assertions.assertEquals(numBeforeDelete-1, buyerService.get().size());
}
}
- 代码中已经有详细的注释,就不多赘述了
验证
- 请再次确认数据库、表、记录都已经准备就绪
- 运行单元测试类,如下图,一切符合预期

- 去数据库看一下,如下图红框所示,那是执行testUpdate方法时更新的结果

- 至此,quarkus连接多个数据库的实战操作已完成,希望这个实用技能可以给您一些参考
源码下载
- 本篇实战的完整源码可在GitHub下载到,地址和链接信息如下表所示(https://github.com/zq2599/blog_demos)
| 名称 | 链接 | 备注 |
|---|---|---|
| 项目主页 | https://github.com/zq2599/blog_demos | 该项目在GitHub上的主页 |
| git仓库地址(https) | https://github.com/zq2599/blog_demos.git | 该项目源码的仓库地址,https协议 |
| git仓库地址(ssh) | git@github.com:zq2599/blog_demos.git | 该项目源码的仓库地址,ssh协议 |
- 这个git项目中有多个文件夹,本次实战的源码在quarkus-tutorials文件夹下,如下图红框

- quarkus-tutorials是个父工程,里面有多个module,本篇实战的module是multi-db-demo,如下图红框

欢迎关注博客园:程序员欣宸
quarkus数据库篇之三:单应用同时操作多个数据库的更多相关文章
- 达梦数据库学习(一、linux操作系统安装及数据库安装)
达梦数据库学习(一.linux操作系统安装及数据库安装) 环境介绍: 使用VM12+中标麒麟V7.0操作系统+达梦8数据库 一.linux系统搭建 本部分没有需要着重介绍,注意安装时基本环境选择&qu ...
- MySQL数据库篇之单表查询
主要内容: 一.单表查询的语法 二.关键字的执行优先级 三.简单查询 四.where约束 五.分组查询 group by 六.having过滤 七.查询排序 order by 八.限制查询的记录数 l ...
- Django之模型层第一篇:单表操作
Django之模型层第一篇:单表操作 一 ORM简介 我们在使用Django框架开发web应用的过程中,不可避免地会涉及到数据的管理操作(如增.删.改.查),而一旦谈到数据的管理操作,就需要用到数 ...
- 【第一篇】ASP.NET MVC快速入门之数据库操作(MVC5+EF6)
目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...
- 6、Django之模型层第一篇:单表操作
一 ORM简介 我们在使用Django框架开发web应用的过程中,不可避免地会涉及到数据的管理操作(如增.删.改.查),而一旦谈到数据的管理操作,就需要用到数据库管理软件,例如mysql.oracle ...
- 数据库之mysql篇(4)—— navicat操作mysql
navicat 1.简介: navicat是一个软件,旗下针对不同数据库有不同的软件版本,支持以下数据库,还是挺厉害的: 这里我采用navicat for mysql版本.实现图形化的操作mysql, ...
- Django中的app及mysql数据库篇(ORM操作)
Django常见命令 在Django的使用过程中需要使用命令让Django进行一些操作,例如创建Django项目.启动Django程序.创建新的APP.数据库迁移等. 创建Django项目 一把我们都 ...
- HelloDjango 系列教程:第 04 篇:Django 迁移、操作数据库
文中涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库 我们已经编写了博客数据库模型的代码,但那还只是 Python 代码而已,django 还没有把它翻译成数据库语言,因此实际上这 ...
- jackson学习之三:常用API操作
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- iOS开发数据库篇—SQL
iOS开发数据库篇—SQL 一.SQL语句 如果要在程序运行过程中操作数据库中的数据,那得先学会使用SQL语句 1.什么是SQL SQL(structured query language):结构化查 ...
随机推荐
- 2020-12-23:如何保证MQ的高可用?
福哥答案2020-12-23: ActiveMQ:Master-Slave 部署方式主从热备,方式包括通过共享存储目录来实现(shared filesystem Master-Slave).通过共享数 ...
- 2020-12-13:用最少数量的线程,每个线程执行for的空循环,把cpu打满了。如果在for的空循环里添加打印输出函数,会把cpu打满吗?为什么?
福哥答案2020-12-13:不会.输出会进行io操作,相对于CPU的速度,这是一个非常缓慢的过程,所以CPU会有机会空闲下来.***[评论](https://user.qzone.qq.com/31 ...
- ToolStrip1
Private Sub ToolStrip1_ItemClicked(sender As Object, e As ToolStripItemClickedEventArgs) Handles Too ...
- java.sql.SQLException: The connection property 'useSSL' only accepts values of the form: 'true', 'false', 'yes' or 'no'. The value 'true;' is not in this set.
错误说明: 数据库连接属性"useSSL"的值只能为'true','false','yes','no',多余的都不行 因此我们要到数据库的连接属性(properties)里找答案 ...
- WPF入门教程系列二十四——DataGrid使用示例(2)
WPF入门教程系列目录 WPF入门教程系列二--Application介绍 WPF入门教程系列三--Application介绍(续) WPF入门教程系列四--Dispatcher介绍 WPF入门教程系 ...
- 2023-05-25:给定一个正整数 x,我们将会写出一个形如 x (op1) x (op2) x (op3) x ... 的表达式 其中每个运算符 op1,op2,… 可以是加、减、乘、除之一 例如
2023-05-25:给定一个正整数 x,我们将会写出一个形如 x (op1) x (op2) x (op3) x ... 的表达式 其中每个运算符 op1,op2,- 可以是加.减.乘.除之一 例如 ...
- 代码随想录算法训练营Day16二叉树|104.二叉树的最大深度 559.n叉树的最大深度 111.二叉树的最小深度 222.完全二叉树的节点个数
代码随想录算法训练营 代码随想录算法训练营Day16二叉树|104.二叉树的最大深度 559.n叉树的最大深度 111.二叉树的最小深度 222.完全二叉树的节点个数 104.二叉树的最大深度 题目 ...
- How to use the shell command to get the version of Linux Distributions All In One
How to use the shell command to get the version of Linux Distributions All In One 如何使用 shell 命令获取 Li ...
- 一次 HPC 病毒感染与解决经历
周一的时候,有同事反馈说,HPC 的项目报告路径正在不断产生 *.exe 和 *.pif 文件,怀疑是不是被病毒感染! 收到信息,第一时间进去目录,的确发现该目录每个几秒钟就自动生成一个 *.exe ...
- R 语言中 X11 相关的一些问题
参考 Anaconda 官方文档<Using R language with Anaconda>安装 R-4.0.2: conda create -n r-4.0.2 r-essentia ...