Javalin网站框架介绍之二 - 数据库

本文是《超简单易用的 Java Web 框架 - Javalin》的续篇。

理解了前文所述的基本原理,就能制作一个前后端分离的网站了,但前文没有涉及数据库的问题,下面以 sqlite 为例,说说如何与数据库沟通。

添加依赖

在 pom.xml 中添加以下与数据库操作有关的依赖:

<dependency>
    <groupId>org.xerial</groupId>
    <artifactId>sqlite-jdbc</artifactId>
    <version>3.39.2.0</version>
</dependency>
<dependency>
    <groupId>org.jdbi</groupId>
    <artifactId>jdbi3-core</artifactId>
    <version>3.32.0</version>
</dependency>

代码结构

为了打通数据库与网站各部分的关系,可以添加 4 个文件,形成非常清晰的结构:

  • Entry.java (数据模型,与 Stmt.java 里的数据表结构一一对应)
  • Stmt.java (数据库的 SQL 语句)
  • DB.java (数据库操作集中在这个文件里)
  • Handle.java (调用 DB 的方法进行增删改查)

Entry.java

源码参考 github.com/ahui2016/monostich/.../Entry.java

public record Entry(String id, String notes, String cmd, long created) {
    Map<String, Object> toMap() {
        return Map.of(
                "id", id,
                "notes", notes,
                "cmd", cmd,
                "created", created
        );
    }
}

这里以 Entry 为例,如果数据库里还有别的数据表,则需要创建新的类。

Stmt.java

源码参考 github.com/ahui2016/monostich/.../Stmt.java

public class Stmt {
    public static final String CREATE_TABLES = """
        CREATE TABLE IF NOT EXISTS cmdentry
        (
          id        TEXT   PRIMARY KEY COLLATE NOCASE,
          notes     TEXT   NOT NULL,
          cmd       TEXT   NOT NULL,
          created   INT    NOT NULL
        );
        """;
        
    public static final String GET_ENTRY = """
        SELECT * FROM cmdentry WHERE id = :id;
        """;
}
  • 可见,数据表与数据模型一一对应,这里 cmdentry 对应前面的 Entry 类。
  • 另外有一些增删改查的 SQL 语句 (这里只列出一部分,详见源码)。

DB.java

源码参考 github.com/ahui2016/monostich/.../DB.java

package com.example.myproject;

public class DB {
    private final String dbPath;
    private final Jdbi jdbi;

    public DB(String dbPath) {
        this.path = dbPath;
        this.jdbi = Jdbi.create("jdbc:sqlite:" + dbPath);
        this.jdbi.useHandle(h -> h.createScript(Stmt.CREATE_TABLES).execute());
        this.jdbi.registerRowMapper(ConstructorMapper.factory(Entry.class));
    }

    void insertEntry(Entry entry) {
        jdbi.useHandle(h -> h.createUpdate(Stmt.INSERT_ENTRY)
                .bindMap(entry.toMap())
                .execute());
    }
}
  • 由于有了前面的 Entry 和 Stmt, 现在写 DB.java 就非常方便了。
  • 比如 Stmt.CREATE_TABLESStmt.INSERT_ENTRY 等 SQL 语句集中在一个文件里,很有条理,代码看起来也简明、清晰。
  • 另外由于有了 Entry, 现在向数据库里写入数据、读取数据都很方便,看这里对 Entry 的用法就可以体会。
  • 这里只展示了 insertEntry(), 点击上面源码参考链接可以看到 getEntry(), deleteEntry() 等方法,每个方法都很简短。

Handle.java

源码参考 github.com/ahui2016/monostich/.../Handle.java

public class Handle {

    static DB db = new DB("db/monostich.sqlite");

    static void testDB() {
        // 插入数据
        for (var entry: Mock.entries) {
            db.insertEntry(entry);
        }

        // 获取并在终端打印数据
        var entries = db.getAllEntries();
        for (var entry: entries) {
            Print.ln(entry.toString());
        }

        // 删除数据
        for (var entry: Mock.entries) {
            db.deleteEntry(entry.id());
        }

        // 确认数据已删除
        var emptyEntries = db.getAllEntries();
        Print.ln("size: " + emptyEntries.size());
    }

    class Print {
        public static void ln(String s) {
            System.out.println(s);
        }
    }
}
  • 在 Handle 中,主要是接收来自前端的数据,保存到数据库中,或从数据库获取数据,发送给前端。
  • 这里只是为了说明与数据库的沟通方法,因此不与前端交互,专注于数据库的操作。
  • Mork.entries 只是一个简单的 Map, 详见上面源码参考链接。

总结

如上所示,数据模型 (Entry.java) 与数据表一一对应,然后在 DB.java 里通过 Jdbi 进行数据库操作,最后在 Handle.java 中调用 DB 的方法,形成非常有条理的代码结构。

2
1