优秀的编程知识分享平台

网站首页 > 技术文章 正文

JsonDB:一个基于 Java 的开源数据库

nanyue 2025-01-09 15:08:58 技术文章 3 ℃

将其数据存储为 .json 文件

内存占用小,嵌入在 Java 程序中运行,

提供在名称和语义上与 Spring Data for MongoDB 非常相似的 API,

支持数据加密、基于 XPath 的搜索/查找查询。

为什么选择 Jsondb?

您有一个基于 Java 的程序(甚至 Android 应用程序)并且您:

  • 需要某种轻量级数据库来存储少量(几千行)数据;
  • 需要一个纯java、易用、线程安全的数据库;
  • 需要一些 ORM 支持;
  • 需要一个好的查询接口;
  • 好的 Mongodb 就像每个 API 级别的原子性(好的最终数据一致性);
  • 希望能够方便地添加、编辑、更新、删除db数据文件,即使Jsondb正在运行时;
  • 希望能够无缝移动数据库文件,在数据库运行时通过网络下载更新的文件;
  • 希望能够通过一些高强度加密来保护数据库数据文件中的敏感数据(密码等)。

Jsondb的不适用场景

  • Jsondb 将数据库数据文件加载到内存中,所有 API 都会返回深度克隆的对象,因此您需要的内存至少与数据文件的大小相同,甚至更多,
  • Jsondb 没有完整的事务支持(像原子 API 一样支持 Mongodb)

如何获取?

Jsondb 构建已上传到 Maven Central仓库

<dependency>
    <groupId>io.jsondb</groupId>
    <artifactId>jsondb-core</artifactId>
    <version>1.0.106</version>
</dependency>

使用Jsondb

了解 JsonDB 用法的最佳方法是查看其源代码中许多 Junit 测试的代码。这些 junit 测试类涵盖了所有可能的场景。

Jsondb 中使用的主要类是:io.jsondb.JsonDBTemplate

JsonDBTemplate 的使用方式与 Spring Data MongoTemplate 类非常相似。

实例化 JsonDBTemplate

//数据库文件在磁盘上的实际位置,进程应该对此文件夹具有读写权限
String dbFilesLocation = "<path on disk to save or read .json files from>";

//POJO的Java包名称
String baseScanPackage = "com.leadsoft.model";

//可选项:如果需要加密,可以选择加密对象
ICipher cipher = new Default1Cipher("1r8+24pibarAWgS85/Heeg==");

JsonDBTemplate jsonDBTemplate = new JsonDBTemplate(dbFilesLocation, baseScanPackage, cipher);

定义 POJO

您希望保存在 JsonDB 中的每个对象都必须是满足 JavaBean 编程约定的 POJO(普通旧 Java 对象)。每个对象必须实现可序列化或外部化,必须具有无参数(默认)构造函数,并且其每个属性(属性)必须具有公共 setter 和 getter 方法。这些方法的名称必须符合 JavaBean 命名约定。

请注意,POJO 中的包与我们在实例化 JsonDBTemplate 对象时传递的第二个参数相同。

另请注意 POJO 中的 3 个注释:

@Document :该注解表示该 POJO 是一个 Jsondb Collection,属性 collection 指定磁盘上 .json 文件的名称,属性 schemaVersion 指定 schema版本此支持尚未完成。

@Id :该注解表示该字段是集合中的主键。Jsondb 每个集合仅支持一个 Id 字段。

@Secret :该注释表示该字段的值在写入磁盘之前应进行加密。

package com.mypackage.model;

import io.jsondb.annotation.Document;
import io.jsondb.annotation.Id;
import io.jsondb.annotation.Secret;

/**
*一个测试Pojo,表示一个测试类Instance。
*/
@Document(collection = "instances", schemaVersion= "1.0")
public class Instance {
    //此字段将用作主键,每个POJO都应该有一个
    @Id
    private String id;
    private String hostname;
    // 此字段将使用提供的密码进行加密
    @Secret
    private String privateKey;

    public String getId() { return id; }
    public void setId(String id) { this.id = id; }
    public String getHostname() { return hostname; }
    public void setHostname(String hostname) { this.hostname = hostname; }
    public String getPrivateKey() { return privateKey; }
    public void setPrivateKey(String privateKey) { this.privateKey = privateKey; }
}

如果集合不存在则创建

jsonDBTemplate.createCollection(Instance.class);

将文档插入集合

Instance instance = new Instance();
instance.setId("11");
instance.setHostname("ec2-54-191-11");
instance.setPrivateKey("b87eb02f5dd7e5232d7b0fc30a5015e4");
jsonDBTemplate.insert(instance);

从集合中删除文档

Instance instance = new Instance();
instance.setId("000012");
jsonDBTemplate.remove(instance, Instance.class);

将文档保存到集合中

Instance instance = new Instance();
instance.setId("000015");
jsonDBTemplate.save(instance, Instance.class);

将文档更新插入集合中

Instance instance = new Instance();
instance.setId("07");
instance.setHostname("ec2-54-191-07");
instance.setPrivateKey("PrivateRyanSaved");
instance.setPublicKey("TomHanks");
jsonDBTemplate.upsert(instance);

知道 Id 后查找文档

jsonDBTemplate.findById("000000", Instance.class);

基于 XPath 的查询

XPATH 语法允许使用多种方式来查询和定位感兴趣的文档。下面是几个例子:

查找具有等于条件的文档

String jxQuery = String.format("/.[id='%s']", "01");
List<Instance> instances = jsonDBTemplate.find(jxQuery, Instance.class);

查找不等于条件的文档

String jxQuery = "/.[not(privateKey='')]";
List<Instance> instances = jsonDBTemplate.find(jxQuery, Instance.class);

查找大于条件的文档

String jxQuery = String.format("/.[id>'%s']", "01");
List<Instance> instances = jsonDBTemplate.find(jxQuery, Instance.class);

根据查询作为原子操作查找并删除第一个文档

String jxQuery = String.format("/.[id>'%s']", "04");
Instance removedObjects = jsonDBTemplate.findAndRemove(jxQuery, Instance.class);

根据原子操作查询查找并删除所有文档

String jxQuery = String.format("/.[id>'%s']", "04");
IList<Instance> removedObjects = jsonDBTemplate.findAllAndRemove(jxQuery, Instance.class);

基于查询作为原子操作查找和修改第一个文档

Update update = Update.update("privateKey", "SavingPrivateRyan");
update.set("publicKey", "SavedByPublic");

String jxQuery = String.format("/.[id='%s']", "03");
Instance retInstance = jsonDBTemplate.findAndModify(jxQuery, update, "instances");

根据原子操作查询查找并删除所有文档

Update update = Update.update("privateKey", "SavingPrivateRyan");
update.set("publicKey", "SavedByPublic");

String jxQuery = String.format("/.[id>'%s']", "03");
List<Instance> retInstances = jsonDBTemplate.findAllAndModify(jxQuery, update, "instances");

排序和切片

对 Jsondb 进行排序允许您使用 find() 和 findAll() 方法获得排序结果。

Comparator<Instance> comparator = new Comparator<Instance>() {
    @Override
    public int compare(Instance o1, Instance o2) {
        return (o1.getSomeAttribute().compareTo(o2.getSomeAttribute()));
    }
};
List<Instance> instances = jsonDBTemplate.findAll(Instance.class, comparator);

切片 Jsondb 切片功能与 python/numpy 切片功能类似。切片字符串参数应采用 i:j:k 格式,并且应遵循与 python/numpy 中的切片功能类似的所有规则。

切片可以与排序结合使用,也可以不与排序结合使用

List<Instance> instances = jsonDBTemplate.findAll(Instance.class, null, "0:7:2");

加密支持

  • 如果您不需要加密支持,只需使用不需要密码参数或传递 null 的 JsonDBTemplate 构造函数。确保您没有在任何 POJO 中使用 @Secret 注释。
  • 但是,如果您确实需要加密支持,则如上面的示例所示,实例化 io.jsondb.crypto.Default1Cipher 类的实例并在实例化 JsonDBTemplate 时传递该实例。
  • 如果您需要帮助为上述默认密码创建安全的 Symmetric Key ,请使用以下独立命令。
String base64EncodedKey = CryptoUtil.generate128BitKey("SomeSecretPassword", "SomeUniqueSecreteSalt");
  • 如果您需要 192 位或 256 位安全 Symmetric Key ,请确保您的 JRE\lib\security 文件夹中有无限强度 JCE 策略 jar,然后编写类似于 CryptoUtil.generate128BitKey() .编译并执行它并在实例化 JsonDBTemplate 时使用密钥。
  • 如果您需要自定义加密支持(例如 DES 加密),您可以实现接口 io.jsondb.crypto.ICipher ,然后在实例化 JsonDBTemplate 时使用该接口。
  • 如果您需要更改使用的 cipher 和/或 key ,请使用以下 api。确保备份数据库文件,以防出现问题。
ICipher newCipher = new Default1Cipher("SomeNewKey");
jsonDBTemplate.changeEncryption(newCipher);


代码71

代码 · 目录

上一篇如何用代码实现“隐写术”

Tags:

最近发表
标签列表