quarkus database Part 3: single application operates multiple databases at the same time

Welcome to my GitHub

Here we classify and summarize all the original works of Xinchen (including supporting source code): https://github.com/zq2599/blog_demos

Overview of this article

  • It is a common scenario for an application to connect multiple databases for operation at the same time, and quarkus is no exception. Today, we will practice multi data source operation with this article
  • As shown in the figure below, today we will create an application named multi DB demo, which connects two databases at the same time. The seller table is in the library named fist dB and the buyer table is in the library named second dB
  • In order to simplify the demo, this article continues to insist on not supporting web services, and uses unit tests to verify that there is no problem for the application to operate two databases at the same time

limit

  • There are two ways for quarkus to connect and operate databases: traditional JDBC and reactive. The demo we demonstrated earlier is the traditional JDBC method
  • Up to now (the latest version is 2.9), only JDBC supports multiple data sources, while reactive mode does not

preparation

  • Prepare the environment before actual combat. Since it is a multi-source operation, it is necessary to prepare at least two databases. Please prepare MySQL and PostgreSQL before doing the following data preparation
  • First create a database and table in MySQL database. Refer to SQL as follows
# Build database
CREATE DATABASE first_db;

# Select database
use first_db;

# Build table
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;

# Add three records
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);
copy
  • Then, create database and tables in PostgreSQL. The reference SQL is as follows
# Build database
CREATE DATABASE second_db;

# Build table
CREATE TABLE buyer(
   id SERIAL PRIMARY KEY,
   name VARCHAR NOT NULL,
   order_num int NOT NULL
);

# Add two records
insert into buyer (name, order_num) values ('buyer1', 100);
insert into buyer (name, order_num) values ('buyer2', 200);
copy
  • Then sort out the addresses of the two databases, which will be used later
  1. MySQL: jdbc:mysql://192.168.50.43:3306/first_db
  2. PostgreSQL: jdbc:postgresql://192.168.50.43:15432/second_db

Develop - create subproject

  • One of quarkus actual combat: preparation The parent project has been created. Today, a sub project named multi DB demo is added under the parent project. Its pom is not different from the previous project. MySQL library is added. All dependencies are as follows
    <dependencies>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-arc</artifactId>
        </dependency>
        <!-- JDBC library -->
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-agroal</artifactId>
        </dependency>
        <!-- hibernate library -->
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-hibernate-orm</artifactId>
        </dependency>
        <!-- postgresql library -->
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-jdbc-postgresql</artifactId>
        </dependency>
        <!-- mysql library -->
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-jdbc-mysql</artifactId>
        </dependency>
        <!-- Unit test library -->
        <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>
copy

Development - Profile

  • Next is the key point of multi data source operation: configuration file. In order to meet the needs of multiple profiles, continue to use application Properties and application XXX Properties, application Properties store public configuration, such as database type, while application XXX Properties are configuration items related to each profile environment, such as database IP address, account password, etc., as shown in the following figure
  • Here we will emphasize the content of the configuration: the configuration is the data source, which is the configuration item used when connecting to the database in the code
  • The next step is the configuration item. There are two data sources, so both data source configuration items should be available. Let's configure them one by one
  • The first one is first dB. We regard it as the default data source of the application. Its configuration is not different from that of the original single data source, as shown below
# For the configuration of first dB, the following five configuration items are in application Properties file
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

# For the configuration of first dB, the following three configuration items are in application test In the properties file, that is, the database address, account number, password and other information of Fitst dB in the test environment
quarkus.datasource.username=root
quarkus.datasource.password=123456
quarkus.datasource.jdbc.url=jdbc:mysql://192.168.50.43:3306/first_db
copy
  • Next is second_ For DB configuration, pay attention to the requirements of quarkus for non default data source configuration: the key of the configuration item must have the data source name. The following figure shows the comparison between the default data source and non default data source configuration items. The red content is the data source name, which is placed after the second dot
  • According to the above rules, second_ All configurations of DB are as follows
# second_db configuration, the following five configuration items are in application Properties file
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 configuration. The following three configuration items are in application test In the properties file, that is, second in the test environment_ DB's database address, account number, password and other information
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
copy
  • One more thing to note: quarkus hibernate-orm. Packages and quarkus hibernate-orm. second_db. Packages represent the default data source and second, respectively_ DB is the package of the entity class of each table. When encoding the entity class later, the entity of the seller table can only be placed in com bolingcavalry. multidb. entity. The entity class of firstdb and buyer table can only be placed in com bolingcavalry. multidb. entity. seconddb
  • After the configuration is completed, you can start writing code

Development - Coding

  • Write the entity class first. Note that the package of the entity class should correspond to quarkus hibernate-orm. Packages or quarkus hibernate-orm. second_ db. Packages is the value of these two configuration items
  • The first is first_ The complete source code of the entity class of seller in DB's seller table is as follows. Pay attention to the configuration of the annotation GeneratedValue generated by the primary key
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;
    }
}
copy
  • The first is second_ The complete source code of the entity class of the buyer in the buyer table of DB is as follows. Pay attention to the annotation generated by the primary key and the configuration of 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;
    }
}
copy
  • It can be seen that the above two entity classes have nothing special except that the package should be aligned with the specified value of the configuration item. They are written in the same way regardless of single data source or multiple data sources
  • Next comes the service class. First, look at the service class sellerservice corresponding to the seller table Java, as shown below, since the database corresponding to the seller table is the default database of the current application, there is no need for any special settings related to the data source when operating the database, which is the same as that of the single data source application
@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);
        }
    }
}
copy
  • Then there is the service class buyerservice for the buyer related operations in the buyer table Java, it can be seen that its member variable entityManager has multiple annotations PersistenceUnit, and the value is equal to the database name second in the configuration file_ DB, this annotation ensures that entityManager uses second_db data source, other codes are no different from the operation of single data source
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);
        }
    }
}
copy
  • One thing to pay special attention to: the package of the PersistenceUnit class is Io quarkus. hibernate. ORM, pay attention when import ing
  • After the code is written, the next step is to enter the verification phase, which is still verified by unit tests

Development - unit testing

  • Although there are two service classes (SellerService and BuyerService), there is only one unit test class. This is to simulate the scenario of operating two databases at the same time in practical applications. You can also change to one unit test class for each service class according to your own situation
@QuarkusTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class MultiDBTest {

    /**
     * first_db Number of initial records in the seller table of
     */
    private static final int FIRST_DB_EXIST_RECORDS_SIZE = 3;

    /**
     * second_db Number of initial records in the buyer table of
     */
    private static final int SECOND_DB_EXIST_RECORDS_SIZE = 2;

    /**
     * import.sql id of the first record in the
     */
    private static final int EXIST_FIRST_ID = 1;

    /**
     * In fruit In Java, the SequenceGenerator in the id field specifies that the initialValue is equal to 10,
     * Indicates that the self increment ID starts from 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();
        // Decision not empty
        Assertions.assertNotNull(sellerList);
        // Three new records were added during the initialization of the seller table
        Assertions.assertEquals(FIRST_DB_EXIST_RECORDS_SIZE, sellerList.size());

        List<Buyer> buyerList = buyerService.get();
        // Decision not empty
        Assertions.assertNotNull(buyerList);
        // Two new records were added during initialization of the buyer table
        Assertions.assertEquals(SECOND_DB_EXIST_RECORDS_SIZE, buyerList.size());
    }

    @Test
    @DisplayName("getSingle")
    @Order(2)
    public void testGetSingle() {
        // Use the second record. The first one was changed when the testUpdate method was executed
        Seller seller = sellerService.getSingle(EXIST_FIRST_ID+1);
        // Decision not empty
        Assertions.assertNotNull(seller);
        // The first record in the buyer table
        Assertions.assertEquals("seller2", seller.getName());

        // Use the second record. The first one was changed when the testUpdate method was executed
        Buyer buyer = buyerService.getSingle(EXIST_FIRST_ID+1);
        // Decision not empty
        Assertions.assertNotNull(buyer);
        // The second record in the buyer table
        Assertions.assertEquals("buyer2", buyer.getName());
    }

    @Test
    @DisplayName("update")
    @Order(3)
    public void testUpdate() {
        // Verify first_ Operation of DB
        String newName = LocalDateTime.now().toString();

        Seller seller = new Seller();
        seller.setName(newName);

        // Update database
        sellerService.update(EXIST_FIRST_ID, seller);

        Seller sellerFromDB = sellerService.getSingle(EXIST_FIRST_ID);
        // The name of the object retrieved from the database should be equal to the modified name
        Assertions.assertEquals(newName, sellerFromDB.getName());

        // Verify second_ Operation of DB
        Buyer buyer = new Buyer();
        buyer.setName(newName);

        // Update database
        buyerService.update(EXIST_FIRST_ID, buyer);

        Buyer buyerFromDB = buyerService.getSingle(EXIST_FIRST_ID);
        // The name of the object retrieved from the database should be equal to the modified name
        Assertions.assertEquals(newName, buyerFromDB.getName());
    }

    @Test
    @DisplayName("create")
    @Order(3)
    public void testCreate() {
        Seller seller = new Seller();
        seller.setName("seller4");
        sellerService.create(seller);
        // After successful creation, the record primary key must be greater than 3
        Assertions.assertTrue(seller.getId()>FIRST_DB_EXIST_RECORDS_SIZE);
        // The total number of records should be equal to the number of existing records + 1
        Assertions.assertEquals(FIRST_DB_EXIST_RECORDS_SIZE+1, sellerService.get().size());

        Buyer buyer = new Buyer();
        buyer.setName("buyer3");
        buyerService.create(buyer);
        // After successful creation, the record primary key must be greater than 3
        Assertions.assertTrue(buyer.getId()>SECOND_DB_EXIST_RECORDS_SIZE);
        // The total number of records should be equal to the number of existing records + 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();
        // First record the total number before deletion
        int numBeforeDelete = sellers.size();

        // Delete last record
        sellerService.delete(sellers.get(numBeforeDelete-1).getId());

        // The number of records should be equal to the number before deletion minus one
        Assertions.assertEquals(numBeforeDelete-1, sellerService.get().size());

        List<Buyer> buyers = buyerService.get();

        // First record the total number before deletion
        numBeforeDelete = buyers.size();

        // Delete last record
        buyerService.delete(buyers.get(numBeforeDelete-1).getId());

        // The number of records that should be deleted minus one
        Assertions.assertEquals(numBeforeDelete-1, buyerService.get().size());
    }
}
copy
  • There are already detailed comments in the code, so I won't go into details

verification

  • Please confirm again that the database, tables and records are ready
  • Run the unit test class, as shown in the figure below. Everything is in line with expectations
  • Go to the database and have a look, as shown in the red box below, which is the result of the update when the testUpdate method is executed
  • So far, quarkus has completed some practical operations. I hope you can connect to this database

Source download

  • The complete source code of this actual combat can be downloaded from GitHub. The address and link information are shown in the table below( https://github.com/zq2599/blog_demos)

name

link

remarks

Project Home

https://github.com/zq2599/blog_demos

The project is on the home page of GitHub

git warehouse address (https)

https://github.com/zq2599/blog_demos.git

The warehouse address of the source code of the project, https protocol

git warehouse address (ssh)

git@github.com:zq2599/blog_demos.git

The warehouse address of the source code of the project, ssh protocol

  • There are several folders in this git project. The source code of this actual battle is in the quarkus tutorials folder, as shown in the red box below
  • Quarkus tutorials is a parent project with multiple modules. The actual module in this article is multi DB demo, as shown in the red box below

Overview of this article

  • It is a common scenario for an application to connect multiple databases for operation at the same time, and quarkus is no exception. Today, we will practice multi data source operation with this article
  • As shown in the figure below, today we will create an application named multi DB demo, which connects two databases at the same time. The seller table is in the library named fist dB and the buyer table is in the library named second dB
  • In order to simplify the demo, this article continues to insist on not supporting web services, and uses unit tests to verify that there is no problem for the application to operate two databases at the same time

limit

  • There are two ways for quarkus to connect and operate databases: traditional JDBC and reactive. The demo we demonstrated earlier is the traditional JDBC method
  • Up to now (the latest version is 2.9), only JDBC supports multiple data sources, while reactive mode does not

preparation

  • Prepare the environment before actual combat. Since it is a multi-source operation, it is necessary to prepare at least two databases. Please prepare MySQL and PostgreSQL before doing the following data preparation
  • First create a database and table in MySQL database. Refer to SQL as follows
# Build database
CREATE DATABASE first_db;

# Select database
use first_db;

# Build table
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;

# Add three records
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);
copy
  • Then, create database and tables in PostgreSQL. The reference SQL is as follows
# Build database
CREATE DATABASE second_db;

# Build table
CREATE TABLE buyer(
   id SERIAL PRIMARY KEY,
   name VARCHAR NOT NULL,
   order_num int NOT NULL
);

# Add two records
insert into buyer (name, order_num) values ('buyer1', 100);
insert into buyer (name, order_num) values ('buyer2', 200);
copy
  • Then sort out the addresses of the two databases, which will be used later
  1. MySQL: jdbc:mysql://192.168.50.43:3306/first_db
  2. PostgreSQL: jdbc:postgresql://192.168.50.43:15432/second_db

Develop - create subproject

  • One of quarkus actual combat: preparation The parent project has been created. Today, a sub project named multi DB demo is added under the parent project. Its pom is not different from the previous project. MySQL library is added. All dependencies are as follows
    <dependencies>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-arc</artifactId>
        </dependency>
        <!-- JDBC library -->
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-agroal</artifactId>
        </dependency>
        <!-- hibernate library -->
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-hibernate-orm</artifactId>
        </dependency>
        <!-- postgresql library -->
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-jdbc-postgresql</artifactId>
        </dependency>
        <!-- mysql library -->
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-jdbc-mysql</artifactId>
        </dependency>
        <!-- Unit test library -->
        <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>
copy

Development - Profile

  • Next is the key point of multi data source operation: configuration file. In order to meet the needs of multiple profiles, continue to use application Properties and application XXX Properties, application Properties store public configuration, such as database type, while application XXX Properties are configuration items related to each profile environment, such as database IP address, account password, etc., as shown in the following figure
  • Here we will emphasize the content of the configuration: the configuration is the data source, which is the configuration item used when connecting to the database in the code
  • The next step is the configuration item. There are two data sources, so both data source configuration items should be available. Let's configure them one by one
  • The first one is first dB. We regard it as the default data source of the application. Its configuration is not different from that of the original single data source, as shown below
# For the configuration of first dB, the following five configuration items are in application Properties file
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

# For the configuration of first dB, the following three configuration items are in application test In the properties file, that is, the database address, account number, password and other information of Fitst dB in the test environment
quarkus.datasource.username=root
quarkus.datasource.password=123456
quarkus.datasource.jdbc.url=jdbc:mysql://192.168.50.43:3306/first_db
copy
  • Next is second_ For DB configuration, pay attention to the requirements of quarkus for non default data source configuration: the key of the configuration item must have the data source name. The following figure shows the comparison between the default data source and non default data source configuration items. The red content is the data source name, which is placed after the second dot
  • According to the above rules, second_ All configurations of DB are as follows
# second_db configuration, the following five configuration items are in application Properties file
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 configuration. The following three configuration items are in application test In the properties file, that is, second in the test environment_ DB's database address, account number, password and other information
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
copy
  • One more thing to note: quarkus hibernate-orm. Packages and quarkus hibernate-orm. second_db. Packages represent the default data source and second, respectively_ DB is the package of the entity class of each table. When encoding the entity class later, the entity of the seller table can only be placed in com bolingcavalry. multidb. entity. The entity class of firstdb and buyer table can only be placed in com bolingcavalry. multidb. entity. seconddb
  • After the configuration is completed, you can start writing code

Development: coding

  • Write the entity class first. Note that the package of the entity class should correspond to quarkus hibernate-orm. Packages or quarkus hibernate-orm. second_ db. Packages is the value of these two configuration items
  • The first is first_ The complete source code of the entity class of seller in DB's seller table is as follows. Pay attention to the configuration of the annotation GeneratedValue generated by the primary key
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;
    }
}
copy
  • The first is second_ The complete source code of the entity class of the buyer in the buyer table of DB is as follows. Pay attention to the annotation generated by the primary key and the configuration of 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;
    }
}
copy
  • It can be seen that the above two entity classes have nothing special except that the package should be aligned with the specified value of the configuration item. They are written in the same way regardless of single data source or multiple data sources
  • Next comes the service class. First, look at the service class sellerservice corresponding to the seller table Java, as shown below, since the database corresponding to the seller table is the default database of the current application, there is no need for any special settings related to the data source when operating the database, which is the same as that of the single data source application
@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);
        }
    }
}
copy
  • Then there is the service class buyerservice for the buyer related operations in the buyer table Java, it can be seen that its member variable entityManager has multiple annotations PersistenceUnit, and the value is equal to the database name second in the configuration file_ DB, this annotation ensures that entityManager uses second_db data source, other codes are no different from the operation of single data source
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);
        }
    }
}
copy
  • One thing to pay special attention to: the package of the PersistenceUnit class is Io quarkus. hibernate. ORM, pay attention when import ing
  • After the code is written, the next step is to enter the verification phase, which is still verified by unit tests

Development - unit testing

  • Although there are two service classes (SellerService and BuyerService), there is only one unit test class. This is to simulate the scenario of operating two databases at the same time in practical applications. You can also change to one unit test class for each service class according to your own situation
@QuarkusTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class MultiDBTest {

    /**
     * first_db Number of initial records in the seller table of
     */
    private static final int FIRST_DB_EXIST_RECORDS_SIZE = 3;

    /**
     * second_db Number of initial records in the buyer table of
     */
    private static final int SECOND_DB_EXIST_RECORDS_SIZE = 2;

    /**
     * import.sql id of the first record in the
     */
    private static final int EXIST_FIRST_ID = 1;

    /**
     * In fruit In Java, the SequenceGenerator in the id field specifies that the initialValue is equal to 10,
     * Indicates that the self increment ID starts from 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();
        // Decision not empty
        Assertions.assertNotNull(sellerList);
        // Three new records were added during the initialization of the seller table
        Assertions.assertEquals(FIRST_DB_EXIST_RECORDS_SIZE, sellerList.size());

        List<Buyer> buyerList = buyerService.get();
        // Decision not empty
        Assertions.assertNotNull(buyerList);
        // Two new records were added during initialization of the buyer table
        Assertions.assertEquals(SECOND_DB_EXIST_RECORDS_SIZE, buyerList.size());
    }

    @Test
    @DisplayName("getSingle")
    @Order(2)
    public void testGetSingle() {
        // Use the second record. The first one was changed when the testUpdate method was executed
        Seller seller = sellerService.getSingle(EXIST_FIRST_ID+1);
        // Decision not empty
        Assertions.assertNotNull(seller);
        // The first record in the buyer table
        Assertions.assertEquals("seller2", seller.getName());

        // Use the second record. The first one was changed when the testUpdate method was executed
        Buyer buyer = buyerService.getSingle(EXIST_FIRST_ID+1);
        // Decision not empty
        Assertions.assertNotNull(buyer);
        // The second record in the buyer table
        Assertions.assertEquals("buyer2", buyer.getName());
    }

    @Test
    @DisplayName("update")
    @Order(3)
    public void testUpdate() {
        // Verify first_ Operation of DB
        String newName = LocalDateTime.now().toString();

        Seller seller = new Seller();
        seller.setName(newName);

        // Update database
        sellerService.update(EXIST_FIRST_ID, seller);

        Seller sellerFromDB = sellerService.getSingle(EXIST_FIRST_ID);
        // The name of the object retrieved from the database should be equal to the modified name
        Assertions.assertEquals(newName, sellerFromDB.getName());

        // Verify second_ Operation of DB
        Buyer buyer = new Buyer();
        buyer.setName(newName);

        // Update database
        buyerService.update(EXIST_FIRST_ID, buyer);

        Buyer buyerFromDB = buyerService.getSingle(EXIST_FIRST_ID);
        // The name of the object retrieved from the database should be equal to the modified name
        Assertions.assertEquals(newName, buyerFromDB.getName());
    }

    @Test
    @DisplayName("create")
    @Order(3)
    public void testCreate() {
        Seller seller = new Seller();
        seller.setName("seller4");
        sellerService.create(seller);
        // After successful creation, the record primary key must be greater than 3
        Assertions.assertTrue(seller.getId()>FIRST_DB_EXIST_RECORDS_SIZE);
        // The total number of records should be equal to the number of existing records + 1
        Assertions.assertEquals(FIRST_DB_EXIST_RECORDS_SIZE+1, sellerService.get().size());

        Buyer buyer = new Buyer();
        buyer.setName("buyer3");
        buyerService.create(buyer);
        // After successful creation, the record primary key must be greater than 3
        Assertions.assertTrue(buyer.getId()>SECOND_DB_EXIST_RECORDS_SIZE);
        // The total number of records should be equal to the number of existing records + 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();
        // First record the total number before deletion
        int numBeforeDelete = sellers.size();

        // Delete last record
        sellerService.delete(sellers.get(numBeforeDelete-1).getId());

        // The number of records should be equal to the number before deletion minus one
        Assertions.assertEquals(numBeforeDelete-1, sellerService.get().size());

        List<Buyer> buyers = buyerService.get();

        // First record the total number before deletion
        numBeforeDelete = buyers.size();

        // Delete last record
        buyerService.delete(buyers.get(numBeforeDelete-1).getId());

        // The number of records should be equal to the number before deletion minus one
        Assertions.assertEquals(numBeforeDelete-1, buyerService.get().size());
    }
}
copy
  • There are already detailed comments in the code, so I won't go into details

verification

  • Please confirm again that the database, tables and records are ready
  • Run the unit test class, as shown in the figure below. Everything is in line with expectations
  • Go to the database and have a look, as shown in the red box below, which is the result of the update when the testUpdate method is executed
  • So far, quarkus has completed some practical operations. I hope you can connect to this database

Source download

  • The complete source code of this actual combat can be downloaded from GitHub. The address and link information are shown in the table below( https://github.com/zq2599/blog_demos)

name

link

remarks

Project Home

https://github.com/zq2599/blog_demos

The project is on the home page of GitHub

git warehouse address (https)

https://github.com/zq2599/blog_demos.git

The warehouse address of the source code of the project, https protocol

git warehouse address (ssh)

git@github.com:zq2599/blog_demos.git

The warehouse address of the source code of the project, ssh protocol

  • There are several folders in this git project. The source code of this actual battle is in the quarkus tutorials folder, as shown in the red box below
  • Quarkus tutorials is a parent project with multiple modules. The actual module in this article is multi DB demo, as shown in the red box below

Overview of this article

  • It is a common scenario for an application to connect multiple databases for operation at the same time, and quarkus is no exception. Today, we will practice multi data source operation with this article
  • As shown in the figure below, today we will create an application named multi DB demo, which connects two databases at the same time. The seller table is in the library named fist dB and the buyer table is in the library named second dB
  • In order to simplify the demo, this article continues to insist on not supporting web services, and uses unit tests to verify that there is no problem for the application to operate two databases at the same time

limit

  • There are two ways for quarkus to connect and operate databases: traditional JDBC and reactive. The demo we demonstrated earlier is the traditional JDBC method
  • Up to now (the latest version is 2.9), only JDBC supports multiple data sources, while reactive mode does not

preparation

  • Prepare the environment before actual combat. Since it is a multi-source operation, it is necessary to prepare at least two databases. Please prepare MySQL and PostgreSQL before doing the following data preparation
  • First create a database and table in MySQL database. Refer to SQL as follows
# Build database
CREATE DATABASE first_db;

# Select database
use first_db;

# Build table
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;

# Add three records
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);
copy
  • Then, create database and tables in PostgreSQL. The reference SQL is as follows
# Build database
CREATE DATABASE second_db;

# Build table
CREATE TABLE buyer(
   id SERIAL PRIMARY KEY,
   name VARCHAR NOT NULL,
   order_num int NOT NULL
);

# Add two records
insert into buyer (name, order_num) values ('buyer1', 100);
insert into buyer (name, order_num) values ('buyer2', 200);
copy
  • Then sort out the addresses of the two databases, which will be used later
  1. MySQL: jdbc:mysql://192.168.50.43:3306/first_db
  2. PostgreSQL: jdbc:postgresql://192.168.50.43:15432/second_db

Develop - create subproject

  • One of quarkus actual combat: preparation The parent project has been created. Today, a sub project named multi DB demo is added under the parent project. Its pom is not different from the previous project. MySQL library is added. All dependencies are as follows
    <dependencies>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-arc</artifactId>
        </dependency>
        <!-- JDBC library -->
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-agroal</artifactId>
        </dependency>
        <!-- hibernate library -->
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-hibernate-orm</artifactId>
        </dependency>
        <!-- postgresql library -->
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-jdbc-postgresql</artifactId>
        </dependency>
        <!-- mysql library -->
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-jdbc-mysql</artifactId>
        </dependency>
        <!-- Unit test library -->
        <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>
copy

Development - Profile

  • Next is the key point of multi data source operation: configuration file. In order to meet the needs of multiple profiles, continue to use application Properties and application XXX Properties, application Properties store public configuration, such as database type, while application XXX Properties are configuration items related to each profile environment, such as database IP address, account password, etc., as shown in the following figure
  • Here we will emphasize the content of the configuration: the configuration is the data source, which is the configuration item used when connecting to the database in the code
  • The next step is the configuration item. There are two data sources, so both data source configuration items should be available. Let's configure them one by one
  • The first one is first dB. We regard it as the default data source of the application. Its configuration is not different from that of the original single data source, as shown below
# For the configuration of first dB, the following five configuration items are in application Properties file
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

# For the configuration of first dB, the following three configuration items are in application test In the properties file, that is, the database address, account number, password and other information of Fitst dB in the test environment
quarkus.datasource.username=root
quarkus.datasource.password=123456
quarkus.datasource.jdbc.url=jdbc:mysql://192.168.50.43:3306/first_db
copy
  • Next is second_ For DB configuration, pay attention to the requirements of quarkus for non default data source configuration: the key of the configuration item must have the data source name. The following figure shows the comparison between the default data source and non default data source configuration items. The red content is the data source name, which is placed after the second dot
  • According to the above rules, second_ All configurations of DB are as follows
# second_db configuration, the following five configuration items are in application Properties file
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 configuration. The following three configuration items are in application test In the properties file, that is, second in the test environment_ DB's database address, account number, password and other information
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
copy
  • One more thing to note: quarkus hibernate-orm. Packages and quarkus hibernate-orm. second_db. Packages represent the default data source and second, respectively_ DB is the package of the entity class of each table. When encoding the entity class later, the entity of the seller table can only be placed in com bolingcavalry. multidb. entity. The entity class of firstdb and buyer table can only be placed in com bolingcavalry. multidb. entity. seconddb
  • After the configuration is completed, you can start writing code

Development: coding

  • Write the entity class first. Note that the package of the entity class should correspond to quarkus hibernate-orm. Packages or quarkus hibernate-orm. second_ db. Packages is the value of these two configuration items
  • The first is first_ The complete source code of the entity class of seller in DB's seller table is as follows. Pay attention to the configuration of the annotation GeneratedValue generated by the primary key
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;
    }
}
copy
  • The first is second_ The complete source code of the entity class of the buyer in the buyer table of DB is as follows. Pay attention to the annotation generated by the primary key and the configuration of 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;
    }
}
copy
  • It can be seen that the above two entity classes have nothing special except that the package should be aligned with the specified value of the configuration item. They are written in the same way regardless of single data source or multiple data sources
  • Next comes the service class. First, look at the service class sellerservice corresponding to the seller table Java, as shown below, since the database corresponding to the seller table is the default database of the current application, there is no need for any special settings related to the data source when operating the database, which is the same as that of the single data source application
@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);
        }
    }
}
copy
  • Then there is the service class buyerservice for the buyer related operations in the buyer table Java, it can be seen that its member variable entityManager has multiple annotations PersistenceUnit, and the value is equal to the database name second in the configuration file_ DB, this annotation ensures that entityManager uses second_db data source, other codes are no different from the operation of single data source
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);
        }
    }
}
copy
  • One thing to pay special attention to: the package of the PersistenceUnit class is Io quarkus. hibernate. ORM, pay attention when import ing
  • After the code is written, the next step is to enter the verification phase, which is still verified by unit tests

Development - unit testing

  • Although there are two service classes (SellerService and BuyerService), there is only one unit test class. This is to simulate the scenario of operating two databases at the same time in practical applications. You can also change to one unit test class for each service class according to your own situation
@QuarkusTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class MultiDBTest {

    /**
     * first_db Number of initial records in the seller table of
     */
    private static final int FIRST_DB_EXIST_RECORDS_SIZE = 3;

    /**
     * second_db Number of initial records in the buyer table of
     */
    private static final int SECOND_DB_EXIST_RECORDS_SIZE = 2;

    /**
     * import.sql id of the first record in the
     */
    private static final int EXIST_FIRST_ID = 1;

    /**
     * In fruit In Java, the SequenceGenerator in the id field specifies that the initialValue is equal to 10,
     * Indicates that the self increment ID starts from 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();
        // Decision not empty
        Assertions.assertNotNull(sellerList);
        // Three new records were added during the initialization of the seller table
        Assertions.assertEquals(FIRST_DB_EXIST_RECORDS_SIZE, sellerList.size());

        List<Buyer> buyerList = buyerService.get();
        // Decision not empty
        Assertions.assertNotNull(buyerList);
        // Two new records were added during initialization of the buyer table
        Assertions.assertEquals(SECOND_DB_EXIST_RECORDS_SIZE, buyerList.size());
    }

    @Test
    @DisplayName("getSingle")
    @Order(2)
    public void testGetSingle() {
        // Use the second record. The first one was changed when the testUpdate method was executed
        Seller seller = sellerService.getSingle(EXIST_FIRST_ID+1);
        // Decision not empty
        Assertions.assertNotNull(seller);
        // The first record in the buyer table
        Assertions.assertEquals("seller2", seller.getName());

        // Use the second record. The first one was changed when the testUpdate method was executed
        Buyer buyer = buyerService.getSingle(EXIST_FIRST_ID+1);
        // Decision not empty
        Assertions.assertNotNull(buyer);
        // The second record in the buyer table
        Assertions.assertEquals("buyer2", buyer.getName());
    }

    @Test
    @DisplayName("update")
    @Order(3)
    public void testUpdate() {
        // Verify first_ Operation of DB
        String newName = LocalDateTime.now().toString();

        Seller seller = new Seller();
        seller.setName(newName);

        // Update database
        sellerService.update(EXIST_FIRST_ID, seller);

        Seller sellerFromDB = sellerService.getSingle(EXIST_FIRST_ID);
        // The name of the object retrieved from the database should be equal to the modified name
        Assertions.assertEquals(newName, sellerFromDB.getName());

        // Verify second_ Operation of DB
        Buyer buyer = new Buyer();
        buyer.setName(newName);

        // Update database
        buyerService.update(EXIST_FIRST_ID, buyer);

        Buyer buyerFromDB = buyerService.getSingle(EXIST_FIRST_ID);
        // The name of the object retrieved from the database should be equal to the modified name
        Assertions.assertEquals(newName, buyerFromDB.getName());
    }

    @Test
    @DisplayName("create")
    @Order(3)
    public void testCreate() {
        Seller seller = new Seller();
        seller.setName("seller4");
        sellerService.create(seller);
        // After successful creation, the record primary key must be greater than 3
        Assertions.assertTrue(seller.getId()>FIRST_DB_EXIST_RECORDS_SIZE);
        // The total number of records should be equal to the number of existing records + 1
        Assertions.assertEquals(FIRST_DB_EXIST_RECORDS_SIZE+1, sellerService.get().size());

        Buyer buyer = new Buyer();
        buyer.setName("buyer3");
        buyerService.create(buyer);
        // After successful creation, the record primary key must be greater than 3
        Assertions.assertTrue(buyer.getId()>SECOND_DB_EXIST_RECORDS_SIZE);
        // The total number of records should be equal to the number of existing records + 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();
        // First record the total number before deletion
        int numBeforeDelete = sellers.size();

        // Delete last record
        sellerService.delete(sellers.get(numBeforeDelete-1).getId());

        // The number of records should be equal to the number before deletion minus one
        Assertions.assertEquals(numBeforeDelete-1, sellerService.get().size());

        List<Buyer> buyers = buyerService.get();

        // First record the total number before deletion
        numBeforeDelete = buyers.size();

        // Delete last record
        buyerService.delete(buyers.get(numBeforeDelete-1).getId());

        // The number of records should be equal to the number before deletion minus one
        Assertions.assertEquals(numBeforeDelete-1, buyerService.get().size());
    }
}
copy
  • There are already detailed comments in the code, so I won't go into details

verification

  • Please confirm again that the database, tables and records are ready
  • Run the unit test class, as shown in the figure below. Everything is in line with expectations
  • Go to the database and have a look, as shown in the red box below, which is the result of the update when the testUpdate method is executed
  • So far, quarkus has completed some practical operations. I hope you can connect to this database

Source download

  • The complete source code of this actual combat can be downloaded from GitHub. The address and link information are shown in the table below( https://github.com/zq2599/blog_demos)

name

link

remarks

Project Home

https://github.com/zq2599/blog_demos

The project is on the home page of GitHub

git warehouse address (https)

https://github.com/zq2599/blog_demos.git

The warehouse address of the source code of the project, https protocol

git warehouse address (ssh)

git@github.com:zq2599/blog_demos.git

The warehouse address of the source code of the project, ssh protocol

  • There are several folders in this git project. The source code of this actual battle is in the quarkus tutorials folder, as shown in the red box below
  • Quarkus tutorials is a parent project with multiple modules. The actual module in this article is multi DB demo, as shown in the red box below

Posted by strangebeer on Mon, 23 May 2022 06:18:24 +0300