quarkus資料庫篇之三:單應用同時操作多個資料庫

来源:https://www.cnblogs.com/bolingcavalry/archive/2023/08/17/17624456.html
-Advertisement-
Play Games

### 歡迎訪問我的GitHub > 這裡分類和彙總了欣宸的全部原創(含配套源碼):[https://github.com/zq2599/blog_demos](https://github.com/zq2599/blog_demos) ### 本篇概覽 - 一個應用同時連接多個資料庫進行操作,這是 ...


歡迎訪問我的GitHub

這裡分類和彙總了欣宸的全部原創(含配套源碼):https://github.com/zq2599/blog_demos

本篇概覽

  • 一個應用同時連接多個資料庫進行操作,這是常見的場景,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);
  • 再整理一下兩個資料庫的地址,稍後用到
  1. MySQL:jdbc:mysql://192.168.50.43:3306/first_db
  2. 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地址、賬號密碼等,如下圖
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<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());
    }
}
  • 代碼中已經有詳細的註釋,就不多贅述了

驗證

  • 請再次確認資料庫、表、記錄都已經準備就緒
  • 運行單元測試類,如下圖,一切符合預期
image-20220521101639686
  • 去資料庫看一下,如下圖紅框所示,那是執行testUpdate方法時更新的結果
image-20220521101734758
  • 至此,quarkus連接多個資料庫的實戰操作已完成,希望這個實用技能可以給您一些參考

源碼下載

名稱 鏈接 備註
項目主頁 https://github.com/zq2599/blog_demos 該項目在GitHub上的主頁
git倉庫地址(https) https://github.com/zq2599/blog_demos.git 該項目源碼的倉庫地址,https協議
git倉庫地址(ssh) [email protected]: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);
  • 再整理一下兩個資料庫的地址,稍後用到
  1. MySQL:jdbc:mysql://192.168.50.43:3306/first_db
  2. 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地址、賬號密碼等,如下圖
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<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());
    }
}
  • 代碼中已經有詳細的註釋,就不多贅述了

驗證

  • 請再次確認資料庫、表、記錄都已經準備就緒
  • 運行單元測試類,如下圖,一切符合預期
image-20220521101639686
  • 去資料庫看一下,如下圖紅框所示,那是執行testUpdate方法時更新的結果
image-20220521101734758
  • 至此,quarkus連接多個資料庫的實戰操作已完成,希望這個實用技能可以給您一些參考

源碼下載

名稱 鏈接 備註
項目主頁 https://github.com/zq2599/blog_demos 該項目在GitHub上的主頁
git倉庫地址(https) https://github.com/zq2599/blog_demos.git 該項目源碼的倉庫地址,https協議
git倉庫地址(ssh) [email protected]: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);
  • 再整理一下兩個資料庫的地址,稍後用到
  1. MySQL:jdbc:mysql://192.168.50.43:3306/first_db
  2. 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地址、賬號密碼等,如下圖
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<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());
    }
}
  • 代碼中已經有詳細的註釋,就不多贅述了

驗證

  • 請再次確認資料庫、表、記錄都已經準備就緒
  • 運行單元測試類,如下圖,一切符合預期
image-20220521101639686
  • 去資料庫看一下,如下圖紅框所示,那是執行testUpdate方法時更新的結果
image-20220521101734758
  • 至此,quarkus連接多個資料庫的實戰操作已完成,希望這個實用技能可以給您一些參考

源碼下載

名稱 鏈接 備註
項目主頁 https://github.com/zq2599/blog_demos 該項目在GitHub上的主頁
git倉庫地址(https) https://github.com/zq2599/blog_demos.git 該項目源碼的倉庫地址,https協議
git倉庫地址(ssh) [email protected]:zq2599/blog_demos.git 該項目源碼的倉庫地址,ssh協議
  • 這個git項目中有多個文件夾,本次實戰的源碼在quarkus-tutorials文件夾下,如下圖紅框
    image-20220312091203116
  • quarkus-tutorials是個父工程,裡面有多個module,本篇實戰的module是multi-db-demo,如下圖紅框
    image-20220521103253853

歡迎關註博客園:程式員欣宸

學習路上,你不孤單,欣宸原創一路相伴...


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 加拿大創新、科學和經濟發展部(ISED)已更新和發佈RSS-247 Issue 3標準,取代RSS-247 Issue 2標準。自 Issue 3於發佈之日起 6個月為過渡期,過渡期內Issue 2和Issue 3兩個版本均可使用,過渡期(2023.8.3-2024.2.2)後,僅接受Issue 3 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 前言 垃圾回收(Garbage Collection)是一種記憶體管理機制,用於檢測和清理不再被程式使用的記憶體,這些不再被使用的記憶體就被稱為垃圾。垃圾回收器會在 JS 引擎(瀏覽器或者 nodejs)內部周期性地運行,一般情況下無需開發者手 ...
  • 通常在頁面中嵌套iframe的情況下還需要進行消息傳遞的通信需求。一般分為兩種情況: 1.iframe里的鏈接與父頁面鏈接是非跨域 這種情況處理比較簡單,直接在父級頁面下就可以寫腳本控制iframe里的元素,同時對iframe里的元素進行操作,例如綁定事件,當事件觸發時發送消息給父級頁面。 具體實踐 ...
  • 在 Vue3 中,有許多與響應式相關的函數,例如 toRef、toRefs、isRef、unref 等等。合理地使用這些函數可以在實際開發中大大提高效率。本文將詳細介紹這些函數的用法,讓我們在實際開發中知道應該使用哪些 API 並能夠熟練地回答面試官的相關問題。 ## ref() 大家對於 ref ...
  • 1、安裝tinymce編輯器 npm i tinymcenpm i @tinymce/tinymce-vue 或: yarn add tinymce yarn add @tinymce/tinymce-vue 2、配置中文語言包 地址:中文語言包 註:最好將語言包放在public/langs/或st ...
  • 在本篇技術博文中,我們將深入探討 Uniapp 框架中如何封裝介面,以簡化開發流程並提高效率。介面封裝是一種重要的開發策略,它不僅可以減少代碼量,還能提高代碼的復用性和維護性。 通過閱讀本文,你將深入瞭解 Uniapp 中封裝介面的重要性和優勢,並學會如何實施介面封裝,以提高開發效率和代碼的可維護... ...
  • —————— BEGIN —————— 1、測試對象條件 作為測試對象的醫院信息平臺(或系統)必須具備軟體著作權證書,運行一年以上並通過初驗。 2、標準符合性測試內容包括 3 部分 數據集標準符合性測試 依據標準 WS445-2014、WS 375.9-2012、WS 376.1-2013 的要求, ...
  • 1.3 分層架構演進 1.3.1 傳統四層架構 將領域模型和業務邏輯分離出來,並減少對基礎設施、用戶界面甚至應用層邏輯的依賴,因為它們不屬業務邏輯。將一個夏雜的系統分為不同的層,每層都應該具有良好的內聚性,並且只依賴於比其自身更低的層。 傳統分層架構的基礎設施層位於底層,持久化和消息機制便位於該層。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...