1. 概述

Xodus 是 JetBrains 开源的一款嵌入式数据库。我们可以用它替代传统关系型数据库。使用 Xodus 能获得高性能的事务型键值存储和面向对象的数据模型。这种存储采用仅追加(append-only)机制,最小化随机 I/O 开销,并默认提供快照隔离。

在 Xodus 中,我们拥有快照隔离机制,保证事务内所有读取操作都能获取到整个数据库的一致性快照。 每个提交的事务都会生成一个新的数据库快照(版本),后续事务可以引用该快照。

本教程将全面介绍 Xodus 数据库的核心概念和功能。

2. Xodus 数据处理原理

Xodus 的数据处理流程如下图所示:

Xodus 数据处理流程

这里,Environment 类负责处理日志文件与内存存储间的所有同步操作。EntityStore 类作为环境的封装层,简化了数据操作流程。

3. 环境(Environments)

环境是 Xodus 的底层 API。我们可以将其用作事务型键值存储。下面通过示例仓库演示环境的使用。

3.1. 依赖配置

首先添加 xodus-openAPI 依赖:

<dependency>
    <groupId>org.jetbrains.xodus</groupId>
    <artifactId>xodus-openAPI</artifactId>
    <version>2.0.1</version>
</dependency>

3.2. save() 方法

创建仓库类实现保存逻辑:

public class TaskEnvironmentRepository {
    private static final String DB_PATH = "db.myAppData";
    private static final String TASK_STORE = "TaskStore";

    public void save(String taskId, String taskDescription) {
        try (Environment environment = openEnvironmentExclusively()) {
            Transaction writeTransaction = environment.beginExclusiveTransaction();

            try {
                Store taskStore = environment.openStore(TASK_STORE,
                  StoreConfig.WITHOUT_DUPLICATES, writeTransaction);

                ArrayByteIterable id = StringBinding.stringToEntry(taskId);
                ArrayByteIterable value = StringBinding.stringToEntry(taskDescription);

                taskStore.put(writeTransaction, id, value);
            } catch (Exception e) {
                writeTransaction.abort();
            } finally {
                if (!writeTransaction.isFinished()) {
                    writeTransaction.commit();
                }
            }
        }
    }

    private Environment openEnvironmentExclusively() {
        return Environments.newInstance(DB_PATH);
    }
}

✅ 关键点:

  • 指定数据库文件路径(自动创建)
  • 打开环境并创建独占事务
  • 所有事务必须提交或中止
  • 数据需转换为 ArrayByteIterable 格式

3.3. findOne() 方法

添加查询方法:

public String findOne(String taskId) {
    try (Environment environment = openEnvironmentExclusively()) {
        Transaction readonlyTransaction = environment.beginReadonlyTransaction();

        try {
            Store taskStore = environment.openStore(TASK_STORE,
              StoreConfig.WITHOUT_DUPLICATES, readonlyTransaction);

            ArrayByteIterable id = StringBinding.stringToEntry(taskId);

            ByteIterable result = taskStore.get(readonlyTransaction, id);

            return result == null ? null : StringBinding.entryToString(result);
        } finally {
            readonlyTransaction.abort();
        }
    }
}

⚠️ 注意:

  • 使用只读事务
  • 查询后无需提交,直接中止事务

3.4. findAll() 方法

遍历存储需使用游标

public Map<String, String> findAll() {
    try (Environment environment = openEnvironmentExclusively()) {
        Transaction readonlyTransaction = environment.beginReadonlyTransaction();

        try {
            Store taskStore = environment.openStore(TASK_STORE,
              StoreConfig.WITHOUT_DUPLICATES, readonlyTransaction);

            Map<String, String> result = new HashMap<>();
            try (Cursor cursor = taskStore.openCursor(readonlyTransaction)) {
                while (cursor.getNext()) {
                    result.put(StringBinding.entryToString(cursor.getKey()),
                      StringBinding.entryToString(cursor.getValue()));
                }
            }

            return result;
        } finally {
            readonlyTransaction.abort();
        }
    }
}

✅ 最佳实践:

  • 处理后必须关闭游标
  • 使用只读事务保证一致性

3.5. deleteAll() 方法

实现全量删除:

public void deleteAll() {
    try (Environment environment = openEnvironmentExclusively()) {
        Transaction exclusiveTransaction = environment.beginExclusiveTransaction();

        try {
            Store taskStore = environment.openStore(TASK_STORE,
              StoreConfig.WITHOUT_DUPLICATES, exclusiveTransaction);

            try (Cursor cursor = taskStore.openCursor(exclusiveTransaction)) {
                while (cursor.getNext()) {
                    taskStore.delete(exclusiveTransaction, cursor.getKey());
                }
            }
        } finally {
            exclusiveTransaction.commit();
        }
    }
}

🔧 操作逻辑:

  • 使用游标遍历所有键
  • 逐个删除并提交事务

4. 实体存储(Entity Stores)

实体存储层,我们通过带属性和链接的实体访问数据。该 API 提供了更丰富的查询选项和更高级别的抽象。

4.1. 依赖配置

添加 xodus-entity-store 依赖:

<dependency>
    <groupId>org.jetbrains.xodus</groupId>
    <artifactId>xodus-entity-store</artifactId>
    <version>2.0.1</version>
</dependency>

4.2. save() 方法

先创建模型类:

public class TaskEntity {
    private final String description;
    private final String labels;

    public TaskEntity(String description, String labels) {
        this.description = description;
        this.labels = labels;
    }

    // getters
}

实现保存逻辑:

public class TaskEntityStoreRepository {
    private static final String DB_PATH = "db.myAppData";
    private static final String ENTITY_TYPE = "Task";

    public EntityId save(TaskEntity taskEntity) {
        try (PersistentEntityStore entityStore = openStore()) {
            AtomicReference<EntityId> idHolder = new AtomicReference<>();

            entityStore.executeInTransaction(txn -> {
                final Entity message = txn.newEntity(ENTITY_TYPE);
                message.setProperty("description", taskEntity.getDescription());
                message.setProperty("labels", taskEntity.getLabels());

                idHolder.set(message.getId());
            });

            return idHolder.get();
        }
    }

    private PersistentEntityStore openStore() {
        return PersistentEntityStores.newInstance(DB_PATH);
    }
}

✅ 核心机制:

  • EntityStore 实体仅存在于事务内
  • 需映射为DTO才能在仓库外使用
  • 返回包含实体类型和唯一 ID 的 EntityId

4.3. findOne() 方法

添加查询方法:

public TaskEntity findOne(EntityId taskId) {
    try (PersistentEntityStore entityStore = openStore()) {
        AtomicReference<TaskEntity> taskEntity = new AtomicReference<>();

        entityStore.executeInReadonlyTransaction(
          txn -> taskEntity.set(mapToTaskEntity(txn.getEntity(taskId))));

        return taskEntity.get();
    }
}

映射方法实现:

private TaskEntity mapToTaskEntity(Entity entity) {
    return new TaskEntity(entity.getProperty("description").toString(),
      entity.getProperty("labels").toString());
}

4.4. findAll() 方法

实现全量查询:

public List<TaskEntity> findAll() {
    try (PersistentEntityStore entityStore = openStore()) {
        List<TaskEntity> result = new ArrayList<>();

        entityStore.executeInReadonlyTransaction(txn -> txn.getAll(ENTITY_TYPE)
          .forEach(entity -> result.add(mapToTaskEntity(entity))));

        return result;
    }
}

🚀 优势:

  • 比环境 API 实现更简洁
  • 直接调用 getAll() 方法遍历实体

4.5. deleteAll() 方法

简单粗暴的全量删除:

public void deleteAll() {
    try (PersistentEntityStore entityStore = openStore()) {
        entityStore.clear();
    }
}

✅ 一行代码搞定所有数据清理

5. 总结

本文深入介绍了 JetBrains Xodus 的核心功能,展示了两种主要 API 的基本操作。作为嵌入式数据库方案,Xodus 在特定场景下是传统数据库的有效替代方案。

完整代码示例请查看 GitHub 仓库


原始标题:Introduction to JetBrains Xodus | Baeldung