quarkus数据库篇之三:单应用同时操作多个数据库

2023年 7月 25日 64.0k 0

欢迎访问我的GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):github.com/zq2599/blog…

本篇概览

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

流程图 (7)

  • 为了简化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库,所有依赖如下
        
            
                io.quarkus
                quarkus-arc
            
            
            
                io.quarkus
                quarkus-agroal
            
            
            
                io.quarkus
                quarkus-hibernate-orm
            
            
            
                io.quarkus
                quarkus-jdbc-postgresql
            
            
            
                io.quarkus
                quarkus-jdbc-mysql
            
            
            
                io.quarkus
                quarkus-junit5
                test
            
            
                io.rest-assured
                rest-assured
                test
            
        
    

    开发-配置文件

    • 接下来就是多数据源操作最关键的地方了:配置文件,为了满足多个profile的需要,这里继续使用application.properties和application-xxx.properties组合的方式,application.properties里存放公共配置,例如数据库类型,而application-xxx.properties里面是和各个profile环境有关的配置项,例如数据库IP地址、账号密码等,如下图

    image-20220521083345350

    • 这里再强调一下配置的内容:配置的是数据源(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中都要有数据源名称,下图是默认数据源和非默认数据源配置项的对比,红色内容是数据源名称,放在第二个点号后面

    image-20220521085524659

    • 按照上述规则,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 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 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 sellerList = sellerService.get();
            // 判定非空
            Assertions.assertNotNull(sellerList);
            // seller表初始化时新增了3条记录
            Assertions.assertEquals(FIRST_DB_EXIST_RECORDS_SIZE, sellerList.size());
    
            List 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 sellers = sellerService.get();
            // 先记删除前的总数
            int numBeforeDelete = sellers.size();
    
            // 删除最后一条记录
            sellerService.delete(sellers.get(numBeforeDelete-1).getId());
    
            // 记录数应该应该等于删除前的数量减一
            Assertions.assertEquals(numBeforeDelete-1, sellerService.get().size());
    
            List buyers = buyerService.get();
    
            // 先记删除前的总数
            numBeforeDelete = buyers.size();
    
            // 删除最后一条记录
            buyerService.delete(buyers.get(numBeforeDelete-1).getId());
    
            // 记录数应该应该等于删除前的数量减一
            Assertions.assertEquals(numBeforeDelete-1, buyerService.get().size());
        }
    }
    
    • 代码中已经有详细的注释,就不多赘述了

    验证

    • 请再次确认数据库、表、记录都已经准备就绪
    • 运行单元测试类,如下图,一切符合预期

    image-20220521101639686

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

    image-20220521101734758

    • 至此,quarkus连接多个数据库的实战操作已完成,希望这个实用技能可以给您一些参考

    源码下载

    • 本篇实战的完整源码可在GitHub下载到,地址和链接信息如下表所示(github.com/zq2599/blog…)
    名称 链接 备注
    项目主页 github.com/zq2599/blog… 该项目在GitHub上的主页
    git仓库地址(https) github.com/zq2599/blog… 该项目源码的仓库地址,https协议
    git仓库地址(ssh) git@github.com:zq2599/blog_demos.git 该项目源码的仓库地址,ssh协议
    • 这个git项目中有多个文件夹,本次实战的源码在quarkus-tutorials文件夹下,如下图红框 image-20220312091203116
    • quarkus-tutorials是个父工程,里面有多个module,本篇实战的module是multi-db-demo,如下图红框 image-20220521103253853

    本篇概览

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

    流程图 (7)

    • 为了简化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库,所有依赖如下
        
            
                io.quarkus
                quarkus-arc
            
            
            
                io.quarkus
                quarkus-agroal
            
            
            
                io.quarkus
                quarkus-hibernate-orm
            
            
            
                io.quarkus
                quarkus-jdbc-postgresql
            
            
            
                io.quarkus
                quarkus-jdbc-mysql
            
            
            
                io.quarkus
                quarkus-junit5
                test
            
            
                io.rest-assured
                rest-assured
                test
            
        
    

    开发-配置文件

    • 接下来就是多数据源操作最关键的地方了:配置文件,为了满足多个profile的需要,这里继续使用application.properties和application-xxx.properties组合的方式,application.properties里存放公共配置,例如数据库类型,而application-xxx.properties里面是和各个profile环境有关的配置项,例如数据库IP地址、账号密码等,如下图

    image-20220521083345350

    • 这里再强调一下配置的内容:配置的是数据源(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中都要有数据源名称,下图是默认数据源和非默认数据源配置项的对比,红色内容是数据源名称,放在第二个点号后面

    image-20220521085524659

    • 按照上述规则,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 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 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 sellerList = sellerService.get();
            // 判定非空
            Assertions.assertNotNull(sellerList);
            // seller表初始化时新增了3条记录
            Assertions.assertEquals(FIRST_DB_EXIST_RECORDS_SIZE, sellerList.size());
    
            List 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 sellers = sellerService.get();
            // 先记删除前的总数
            int numBeforeDelete = sellers.size();
    
            // 删除最后一条记录
            sellerService.delete(sellers.get(numBeforeDelete-1).getId());
    
            // 记录数应该应该等于删除前的数量减一
            Assertions.assertEquals(numBeforeDelete-1, sellerService.get().size());
    
            List buyers = buyerService.get();
    
            // 先记删除前的总数
            numBeforeDelete = buyers.size();
    
            // 删除最后一条记录
            buyerService.delete(buyers.get(numBeforeDelete-1).getId());
    
            // 记录数应该应该等于删除前的数量减一
            Assertions.assertEquals(numBeforeDelete-1, buyerService.get().size());
        }
    }
    
    • 代码中已经有详细的注释,就不多赘述了

    验证

    • 请再次确认数据库、表、记录都已经准备就绪
    • 运行单元测试类,如下图,一切符合预期

    image-20220521101639686

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

    image-20220521101734758

    • 至此,quarkus连接多个数据库的实战操作已完成,希望这个实用技能可以给您一些参考

    源码下载

    • 本篇实战的完整源码可在GitHub下载到,地址和链接信息如下表所示(github.com/zq2599/blog…)
    名称 链接 备注
    项目主页 github.com/zq2599/blog… 该项目在GitHub上的主页
    git仓库地址(https) github.com/zq2599/blog… 该项目源码的仓库地址,https协议
    git仓库地址(ssh) git@github.com:zq2599/blog_demos.git 该项目源码的仓库地址,ssh协议
    • 这个git项目中有多个文件夹,本次实战的源码在quarkus-tutorials文件夹下,如下图红框 image-20220312091203116
    • quarkus-tutorials是个父工程,里面有多个module,本篇实战的module是multi-db-demo,如下图红框 image-20220521103253853

    本篇概览

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

    流程图 (7)

    • 为了简化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库,所有依赖如下
        
            
                io.quarkus
                quarkus-arc
            
            
            
                io.quarkus
                quarkus-agroal
            
            
            
                io.quarkus
                quarkus-hibernate-orm
            
            
            
                io.quarkus
                quarkus-jdbc-postgresql
            
            
            
                io.quarkus
                quarkus-jdbc-mysql
            
            
            
                io.quarkus
                quarkus-junit5
                test
            
            
                io.rest-assured
                rest-assured
                test
            
        
    

    开发-配置文件

    • 接下来就是多数据源操作最关键的地方了:配置文件,为了满足多个profile的需要,这里继续使用application.properties和application-xxx.properties组合的方式,application.properties里存放公共配置,例如数据库类型,而application-xxx.properties里面是和各个profile环境有关的配置项,例如数据库IP地址、账号密码等,如下图

    image-20220521083345350

    • 这里再强调一下配置的内容:配置的是数据源(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中都要有数据源名称,下图是默认数据源和非默认数据源配置项的对比,红色内容是数据源名称,放在第二个点号后面

    image-20220521085524659

    • 按照上述规则,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 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 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 sellerList = sellerService.get();
            // 判定非空
            Assertions.assertNotNull(sellerList);
            // seller表初始化时新增了3条记录
            Assertions.assertEquals(FIRST_DB_EXIST_RECORDS_SIZE, sellerList.size());
    
            List 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 sellers = sellerService.get();
            // 先记删除前的总数
            int numBeforeDelete = sellers.size();
    
            // 删除最后一条记录
            sellerService.delete(sellers.get(numBeforeDelete-1).getId());
    
            // 记录数应该应该等于删除前的数量减一
            Assertions.assertEquals(numBeforeDelete-1, sellerService.get().size());
    
            List buyers = buyerService.get();
    
            // 先记删除前的总数
            numBeforeDelete = buyers.size();
    
            // 删除最后一条记录
            buyerService.delete(buyers.get(numBeforeDelete-1).getId());
    
            // 记录数应该应该等于删除前的数量减一
            Assertions.assertEquals(numBeforeDelete-1, buyerService.get().size());
        }
    }
    
    • 代码中已经有详细的注释,就不多赘述了

    验证

    • 请再次确认数据库、表、记录都已经准备就绪
    • 运行单元测试类,如下图,一切符合预期

    image-20220521101639686

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

    image-20220521101734758

    • 至此,quarkus连接多个数据库的实战操作已完成,希望这个实用技能可以给您一些参考

    源码下载

    • 本篇实战的完整源码可在GitHub下载到,地址和链接信息如下表所示(github.com/zq2599/blog…)
    名称 链接 备注
    项目主页 github.com/zq2599/blog… 该项目在GitHub上的主页
    git仓库地址(https) github.com/zq2599/blog… 该项目源码的仓库地址,https协议
    git仓库地址(ssh) git@github.com:zq2599/blog_demos.git 该项目源码的仓库地址,ssh协议
    • 这个git项目中有多个文件夹,本次实战的源码在quarkus-tutorials文件夹下,如下图红框 image-20220312091203116
    • quarkus-tutorials是个父工程,里面有多个module,本篇实战的module是multi-db-demo,如下图红框 image-20220521103253853

    欢迎关注掘金:程序员欣宸

    学习路上,你不孤单,欣宸原创一路相伴...

    相关文章

    KubeSphere 部署向量数据库 Milvus 实战指南
    探索 Kubernetes 持久化存储之 Longhorn 初窥门径
    征服 Docker 镜像访问限制!KubeSphere v3.4.1 成功部署全攻略
    那些年在 Terraform 上吃到的糖和踩过的坑
    无需 Kubernetes 测试 Kubernetes 网络实现
    Kubernetes v1.31 中的移除和主要变更

    发布评论