quarkus数据库篇之一:比官方demo更简单的基础操作

这篇具有很好参考价值的文章主要介绍了quarkus数据库篇之一:比官方demo更简单的基础操作。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

欢迎访问我的GitHub

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

关于《数据库篇》

  • 《quarkus数据库篇》系列是《quarkus实战》的子系列,目标是与大家一起在quarkus框架下完成常用的数据库操作,如配置、增删改查、事物等

本篇概览

  • 本篇敢号称比官方demo更简单,是因为官方关于操作数据库的demo中还有web服务的代码(如接收http请求和响应,以及web库的依赖),而本篇不会有这些代码和依赖,只有存粹的数据库操作和对应的单元测试类,至于web服务?欣宸应该会出《quarkus之web篇》吧(如果时间允许)

  • 作为《数据库篇》的开篇,为了避免长文劝退大多数人的悲剧发生,本文被死死压制在Hello World级别,咱们用最简单的配置和代码完成数据库的增删改查操作,掌握quarkus下基本数据库操作全掌握,然后在后续文章中逐步深入,整体上就是一次从入门到精通之旅

  • 本篇的具体内容是创建一个maven工程,此工程有内容是

  1. 一个单表的实体类
  2. 实体类对应的service类,提供单表增删改查的API
  3. service类对应的单元测试类,一共就这些内容
  • 来看看实际的文件和位置,如下图
quarkus数据库篇之一:比官方demo更简单的基础操作
  • 没错,这个工程就这么简单,官方demo好歹还做了web接口,可以用postman做增删改查的测试,在本篇中这些统统砍掉,只有service层及其单元测试类

环境和版本信息

  • 电脑:MacBook Pro M1,macOS Monterey
  • jdk:11.0.14.1
  • maven:3.8.5
  • quarkus:与《quarkus实战》系列保持一致,依旧是2.7.3.Final
  • 数据库:使用PostgreSQL,版本13.3

源码下载

  • 本篇实战的完整源码可在GitHub下载到,地址和链接信息如下表所示(https://github.com/zq2599/blog_demos)
名称 链接 备注
项目主页 https://github.com/zq2599/blog_demos 该项目在GitHub上的主页
git仓库地址(https) https://github.com/zq2599/blog_demos.git 该项目源码的仓库地址,https协议
git仓库地址(ssh) git@github.com:zq2599/blog_demos.git 该项目源码的仓库地址,ssh协议
  • 这个git项目中有多个文件夹,本次实战的源码在quarkus-tutorials文件夹下,如下图红框
    quarkus数据库篇之一:比官方demo更简单的基础操作
  • quarkus-tutorials是个父工程,里面有多个module,本篇实战的module是basic-db,如下图红框
    quarkus数据库篇之一:比官方demo更简单的基础操作

确认数据库已就绪

  • 请确认PostgreSQL数据库已经就绪

  • 开发阶段推荐用docker部署数据库,简单省事儿,参考命令如下,请将/xxx换为您自己的宿主机目录,用于保存数据库文件

docker run \
--name quarkus_test \
-e POSTGRES_USER=quarkus \
-e POSTGRES_PASSWORD=123456 \
-e POSTGRES_DB=quarkus_test \
-p 5432:5432 \
-v /xxx:/var/lib/postgresql/data \
postgres:13.3
  • 需要在PostgreSQL提前创建名为quarkus_test的数据库,不用建表
  • 在开发过程中可能要连上数据库查看数据,请自行准备客户端工具(命令行也行),我这里用的是IDEA自带的数据库工具,如下图,已连上PostgreSQL的quarkus_test数据库,里面空空如也

quarkus数据库篇之一:比官方demo更简单的基础操作

新建maven子工程basic-db

  • 在父工程quarkus-tutorials下面新建名为basic-db的子项目,其pom.xml内容如下,重点是JDBC、hibernate、postgresql这三个和数据库有关的库
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>quarkus-tutorials</artifactId>
        <groupId>com.bolingcavalry</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>basic-db</artifactId>
    <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>
        <!-- 单元测试库 -->
        <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>

    <build>
        <plugins>
            <plugin>
                <groupId>${quarkus.platform.group-id}</groupId>
                <artifactId>quarkus-maven-plugin</artifactId>
                <extensions>true</extensions>
                <executions>
                    <execution>
                        <goals>
                            <goal>build</goal>
                            <goal>generate-code</goal>
                            <goal>generate-code-tests</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <compilerArgs>
                        <arg>-parameters</arg>
                    </compilerArgs>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <systemPropertyVariables>
                        <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
                        <maven.home>${maven.home}</maven.home>
                    </systemPropertyVariables>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

配置文件

  • 本次实战会用到Hibernate自动重新建表的功能,此功能会先删除库中已存在的同名表,因此,只有一个profile配置的时候,不要让此应用连接到生产环境
  • 最安全的做法是使用profile功能将生产环境和测试环境的配置文件分开,测试环境的配置文件中,是测试数据库,并且开启了自动重新建表的的功能,而生产环境的配置文件中,自动重新建表的功能是关闭的
  • 先来看公共配置文件application.properties,此文件和profile无关,应用一定会加载,里面是各个profile都会用到的公共配置,例如数据库类型
quarkus.datasource.db-kind=postgresql
quarkus.hibernate-orm.log.sql=true
quarkus.datasource.jdbc.max-size=8
quarkus.datasource.jdbc.min-size=2
  • 再看application-test.properties,这是当profile等于test时才会用到的配置文件,有两处要注意的地方稍后会提到
quarkus.datasource.username=quarkus
quarkus.datasource.password=123456
quarkus.datasource.jdbc.url=jdbc:postgresql://192.168.50.43:15432/quarkus_test
quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.hibernate-orm.sql-load-script=import.sql
  • 上述配置,有以下两处值得重视的配置项
  1. quarkus.hibernate-orm.database.generation:有六个取值,如下表
取值 含义
none 啥也不做
create 第一次启动会建表,之后启动不会再改动
drop-and-create 每一次启动应用的时候都删表(数据也没了),然后建表,再执行import.sql导入数据
drop 启动应用的时候删表,不删库
update 保留数据,升级表结构
validate 检查表结构与entity是否匹配
  • 从上表可以看出,drop-and-create这个配置很适合开发和测试阶段,因为每次都会整理好数据,让测试和验证不受历史数据的影响
  • 由于drop-and-createupdate会改动数据库,因此不适合生产环境使用,这一点要牢记,官方也给出了警告

quarkus数据库篇之一:比官方demo更简单的基础操作

  1. quarkus.hibernate-orm.sql-load-script:指定sql文件,在配置项quarkus.hibernate-orm.database.generation等于drop-and-create的时候,就执行此sql文件,可以用来生成初始化数据
  • 配置完成了,接下来开始写代码,从最核心的实体类开始

SQL文件

  • 刚才的配置文件中配合的import.sql,其放置位置与applicatin.properites文件相同,内容如下,可见是往known_fruits表写入了三条记录
INSERT INTO known_fruits(id, name) VALUES (1, 'Cherry');
INSERT INTO known_fruits(id, name) VALUES (2, 'Apple');
INSERT INTO known_fruits(id, name) VALUES (3, 'Banana');
  • 从前面的配置可知,profile等于test的时候,应用启动的时候,会根据实体类的信息执行删表和建表的操作,然后执行import.sql导入三条记录

编码:实体类

  • 熟悉hibernate的读者都知道,实体类并非只有get和set方法的Pojo,它包含了大量的JPA元信息,是应用与数据库表映射的关键
  • 实体类Fruit.java如下,有几处要注意的地方稍后会提到
package com.bolingcavalry.db.entity;

import javax.persistence.Cacheable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.QueryHint;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

@Entity
@Table(name = "known_fruits")
@NamedQuery(name = "Fruits.findAll", query = "SELECT f FROM Fruit f ORDER BY f.name", hints = @QueryHint(name = "org.hibernate.cacheable", value = "true"))
@Cacheable
public class Fruit {

    @Id
    @SequenceGenerator(name = "fruitsSequence", sequenceName = "known_fruits_id_seq", allocationSize = 1, initialValue = 10)
    @GeneratedValue(generator = "fruitsSequence")
    private Integer id;

    @Column(length = 40, unique = true)
    private String name;

    public Fruit() {
    }

    public Fruit(String name) {
        this.name = name;
    }

    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;
    }
}
  • 上述代码有以下几处要注意的
  1. 注解Table确定了表名known_fruits
  2. 增加了一个自定义SQL,名为Fruits.findAll,后面会用到
  3. 注解SequenceGenerator定义了known_fruits的自增主键的信息,初始值是10,也就是说通过当前应用新增的第一条记录,ID等于10
  4. known_fruits表只有两个字段:id和name

service层

  • 为known_fruits表的操作增加一个服务类,用于上层的调用(所谓上层是指web接口、gRPC接口、消息消费入口等)
  • 服务类名为FruitService.java,为了省事儿就直接用class,不写interface了,代码如下,增删改查服务其实就是EntityManager的基本操作,这就不赘述了:
package com.bolingcavalry.db.service;

import com.bolingcavalry.db.entity.Fruit;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.transaction.Transactional;
import java.util.List;

@ApplicationScoped
public class FruitService {
    @Inject
    EntityManager entityManager;

    public List<Fruit> get() {
        return entityManager.createNamedQuery("Fruits.findAll", Fruit.class)
                .getResultList();
    }

    public Fruit getSingle(Integer id) {
        return entityManager.find(Fruit.class, id);
    }

    @Transactional
    public void create(Fruit fruit) {
        entityManager.persist(fruit);
    }

    @Transactional
    public void update(Integer id, Fruit fruit) {
        Fruit entity = entityManager.find(Fruit.class, id);

        if (null!=entity) {
            entity.setName(fruit.getName());
        }
    }

    @Transactional
    public void delete(Integer id) {
        Fruit entity = entityManager.getReference(Fruit.class, id);

        if (null!=entity) {
            entityManager.remove(entity);
        }
    }
}
  • 代码写到这里其实已经完成了,当前工程已经有了数据库增删改查的能力,至于上层如何使用(是web调用、gRPC调用、消费消息),那并非本篇的重点,您可以根据自己需要随意添加
  • 为了验证服务类功能正常,接下来会写一个单元测试类 ,调用FruitService的各API并验证数据是否符合预期

单元测试类

  • 单元测试类只有一个,位置在quarkus-tutorials/basic-db/src/test/java,这是符合maven规范的测试类位置
  • FruitServiceTest源码如下,有几处要注意的地方稍后会提到
package com.bolingcavalry;

import com.bolingcavalry.db.entity.Fruit;
import com.bolingcavalry.db.service.FruitService;
import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.*;

import javax.inject.Inject;
import java.util.List;

@QuarkusTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class FruitServiceTest {

    /**
     * import.sql中导入的记录数量,这些是应用启动是导入的
     */
    private static final int EXIST_RECORDS_SIZE = 3;

    /**
     * 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
    FruitService fruitService;

    @Test
    @DisplayName("list")
    @Order(1)
    public void testGet() {
        List<Fruit> list = fruitService.get();
        // 判定非空
        Assertions.assertNotNull(list);
        // import.sql中新增3条记录
        Assertions.assertEquals(EXIST_RECORDS_SIZE, list.size());
    }

    @Test
    @DisplayName("getSingle")
    @Order(2)
    public void testGetSingle() {
        Fruit fruit = fruitService.getSingle(EXIST_FIRST_ID);
        // 判定非空
        Assertions.assertNotNull(fruit);
        // import.sql中的第一条记录
        Assertions.assertEquals("Cherry", fruit.getName());
    }

    @Test
    @DisplayName("update")
    @Order(3)
    public void testUpdate() {
        String newName = "ShanDongBigCherry";

        fruitService.update(EXIST_FIRST_ID, new Fruit(newName));

        Fruit fruit = fruitService.getSingle(EXIST_FIRST_ID);
        // 从数据库取出的对象,其名称应该等于修改的名称
        Assertions.assertEquals(newName, fruit.getName());
    }

    @Test
    @DisplayName("create")
    @Order(4)
    public void testCreate() {
        Fruit fruit = new Fruit("Orange");
        fruitService.create(fruit);
        // 由于是第一次新增,所以ID应该等于自增ID的起始值
        Assertions.assertEquals(ID_SEQUENCE_INIT_VALUE, fruit.getId());
        // 记录总数应该等于已有记录数+1
        Assertions.assertEquals(EXIST_RECORDS_SIZE+1, fruitService.get().size());
    }

    @Test
    @DisplayName("delete")
    @Order(5)
    public void testDelete() {
        // 先记删除前的总数
        int numBeforeDelete = fruitService.get().size();

        // 删除第一条记录
        fruitService.delete(EXIST_FIRST_ID);

        // 记录数应该应该等于删除前的数量减一
        Assertions.assertEquals(numBeforeDelete-1, fruitService.get().size());
    }
}
  • 上述单元测试类有以下几处要注意
  1. 一共五个测试方法,为了给它们排序,要用注解TestMethodOrder修饰类,并制定value为MethodOrderer.OrderAnnotation.class
  2. 再在每个方法上用Order注解修饰,就可以用value执行测试顺序了
  3. 测试方法有点多,为了便于观察,用注解DisplayName为每个测试方法起了个名字,有了名字,IDEA上的测试结果效果如下
quarkus数据库篇之一:比官方demo更简单的基础操作
  • 单元测试代码写完了,是不是可以立即开始测试了?别急,还有个小坑,有一定几率遇到,别看坑小,要是掉进去还有点麻烦...

IDEA的小坑

  • 回顾之前的配置,数据库信息都放在application-test.properties文件中,因此只有profile等于test时,才有数据库配置信息,其他profile都没有对应的配置文件
  • 一般情况下,如何执行单元测试呢?欣宸的习惯是直接点击下图红框中的按钮,在弹出的菜单上选择第一项Run ‘FruitServiceTest’,这样操作简单,又能通过IDEA界面观察测试结果
quarkus数据库篇之一:比官方demo更简单的基础操作
  • 实测发现,使用上述方式,IDEA给我们设置的profile可能不是test,而是default,而default这个profile的配置文件是不存在的,因此单元测试启动就会失败

  • 上述问题,我这边偶尔遇到过几次,目前无法稳定复现,针对此问题的解决方法如下

  • 点击图标运行单元测试的时候,选择下图红框中的选项

quarkus数据库篇之一:比官方demo更简单的基础操作

  • 在弹出的配置窗口中,新增下图红框中的内容,这就指定了profile等于test

quarkus数据库篇之一:比官方demo更简单的基础操作

  • 运行的时候,选择上图配置的名字FruitServiceTest(test-profile),就能确保profile是test了

运行单元测试

  • 运行单元测试,结果如下图,不但测试全部通过,输出的日志内容也非常丰富,解读他们,是温习前面知识点的最佳手段

quarkus数据库篇之一:比官方demo更简单的基础操作

  • 还有一处要注意的,就是上图显示getSingle方法耗时仅6ms,例外,getSingle执行的时候也没有SQL日志输出,这是因为getSingleb并没有真正的查询数据库,而是使用了前面list的缓存结果,验证是否使用了缓存很简单,将testGet和testGetSingle两个方法的执行顺序调换一下,再执行,就发现testGetSingle执行耗时也变长了,而且SQL日志也出现了
  • 上述这种不查数据库而走本地缓存的操作,虽然看似提升了性能,然而风险也不小,getSingle得到的结果并非数据库中最新的,关闭缓存的方法如下图,修改Fruit.java的配置,如下图

quarkus数据库篇之一:比官方demo更简单的基础操作

  • 至此,相比官方demo更加精简的quarkus数据库操作入门已完成,希望本篇能让咱们对quarkus的数据库操作能力和流程有基本的认识,为接下来的逐渐深入打好基础

欢迎关注博客园:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...文章来源地址https://www.toymoban.com/news/detail-648337.html

到了这里,关于quarkus数据库篇之一:比官方demo更简单的基础操作的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【MySQL系列】数据库基础学习_简单认识数据库

    「前言」文章内容大致是数据库基础,以及数据库的基本知识。 「归属专栏」MySQL 「主页链接」个人主页 「笔者」枫叶先生(fy) 「枫叶先生有点文青病」「句子分享」 我见青山多妩媚,料青山、见我应如是。 ——辛弃疾《贺新郎》 MySQL实际上是一个网络服务(client/server模式

    2024年02月15日
    浏览(46)
  • 【数据库集群】之一主一从

    集群架构设计的三个维度:可用性、扩展性、一致性 所谓mysql集群就是把多台数据库集成在一起提供数据库服务。 集群的目的: 负载均衡:解决高并发 高可用HA:服务可用性 远程备灾:数据有效性 对于数据库之上的业务层来说,基于 MySQL 的主从复制集群,单点写入 Master

    2024年02月01日
    浏览(48)
  • Android GreenDao数据库升级(附Demo)

           大家好久不见,一转眼马上八月份下旬了,最近由于工作比较忙,没时间给大家更新博文。百忙之中抽出时间,给大家来更新一篇关于 GreenDao3+数据库的升级 。        关于GreenDao的详细介绍以及一些逻辑性的 增、删、改、查 等,可以参考我去年写的一篇文章,

    2024年02月11日
    浏览(45)
  • 基于Spring注解 + MyBatis + Servlet 实现数据库交换的小小Demo

    配置数据库连接信息 db.properties 配置web.xml 配置logback.xml配置文件 配置applicationContext.xml 里面的bean 配置myBatis核心配置文件mybatis-config.xml 创建实体类对象User 创建LoginServlet响应前端的数据 创建UserService 接口 创建UserMapper接口 创建UserServiceImpl 接口实现类 按照这样的方式进行拼接

    2024年02月02日
    浏览(85)
  • 基于MybatisPlus拦截器实现数据库关键字处理及官方做法

    有些老的数据库当中可能会有些字段和数据库冲突,使用mybatisPlus执行Sql的时候有时候会执行失败,前段时间和群友讨论的时候他说遇到了这个问题,当时我提议让他用我以前写的一个自定义注解+mybatis拦截器实现权限控制里边的工具类改造一下。 他说不能实现,然后

    2024年04月25日
    浏览(41)
  • H2数据库配置及相关使用方式一站式介绍(极为详细并整理官方文档)

    注意:可以直接移步至【2. H2数据库实战】目录下进行对H2数据库的快速使用 1.1 H2数据库介绍 官方文档地址: http://www.h2database.com/html/main.html H2的开发始于2004年5月, 但它在2005年12月14日首次发表。 H2的原作者Thomas Mueller也是Hypersonic SQL的原始开发者。 2001年,他加入PointBase公司

    2024年02月13日
    浏览(44)
  • quarkus实战之一:准备工作

    这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 《quarkus实战》系列是欣宸在2022年春季推出的又一个精品原创系列,作者将自己对java的热爱渗透到每段文字和每行代码中,全系列秉承欣宸一贯的知识+实战风格,既有知识普及、更有实际操作,在涉

    2024年02月16日
    浏览(43)
  • quarkus依赖注入之一:创建bean

    这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 对一名java程序员来说,依赖注入应该是个熟悉的概念,简单的说就是:我要用XXX,但我不负责XXX的生产 以下代码来自spring官方,serve方法要使用MyComponent类的doWork方法,但是不负责MyComponent对象的实

    2024年02月15日
    浏览(41)
  • 【数据库】简单聊聊数据库可以做什么,有什么用?

    随着云计算和大数据的兴起,数据库的作用日益突出,因此了解什么是数据库,数据库可以做什么,有什么用是非常必要的。今天我们就来简单聊聊。 数据库是什么? 数据库是一个组织化的数据集合,用于存储大量的结构化数据。通过数据模型、数据结构和数据管理的概念

    2024年01月24日
    浏览(49)
  • 简单认识数据库用户管理

    格式 :CREATE USER ‘用户名’@‘来源地址’ [IDENTIFIED BY [PASSWORD] ‘密码’]; 用户名 :指定将创建的用户名. 来源地址 :指定新创建的用户可在哪些主机上登录,可使用IP地址、网段、主机名的形式,本地用户可用localhost,允许任意主机登录可用通配符% 密码 :若使用明文密码

    2024年02月16日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包