在本文中,我们探索了 JetBrains Xodus。我们研究了此数据库的主要 API,并演示了如何使用它执行基本操作。此数据库可以成为嵌入式数据库集的宝贵补充。
Xodus是 JetBrains 构建的开源嵌入式数据库。我们可以将其用作关系数据库的替代品。使用 Xodus,我们可以获得高性能事务键值存储和面向对象的数据模型。此存储具有仅附加机制,可最大限度地减少随机 IO 开销,并默认提供快照隔离。
在 Xodus 中,我们拥有快照隔离,可保证事务内所有读取操作的整个数据库快照一致。对于每个已提交的事务,我们将获得数据库的新快照(版本),使后续事务可以引用此快照。
在本教程中,我们将介绍 Xodus 数据库概念的大部分功能。
我们将开始添加xodus-openAPI依赖项:
<dependency> <groupId>org.jetbrains.xodus</groupId> <artifactId>xodus-openAPI</artifactId> <version>2.0.1</version> </dependency>
|
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); } }
|
在这里,我们指定了数据库文件目录的路径——所有文件都将自动创建。然后,我们打开环境并创建独占转换——所有事务都应在数据处理后提交或中止。
之后,我们创建了 store 来操作数据。使用 store,我们将 ID 和值放入数据库。将所有数据转换为ArrayByteIterable很重要。
findOne()
现在,让我们将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(); } } }
|
在这里,我们同样创建环境实例。由于我们正在实现读取操作,因此我们将在这里使用只读事务。我们调用商店实例的get()方法通过 ID 获取任务描述。读取操作后,我们没有任何内容要提交,因此我们将中止事务。
indAll()
要迭代存储,我们需要使用Cursors。让我们使用游标实现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(); } } }
|
在只读事务中,我们打开了存储并创建了游标,没有任何标准。然后,迭代存储,我们用所有 ID 和任务描述的组合填充地图。处理后关闭游标很重要。
删除全部()
现在,我们将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(); } } }
|
在此实现中,我们采用相同的方法使用游标来迭代所有项目。对于每个项目键,我们调用 存储的 delete()方法。最后,我们提交所有更改。
实体存储层
在实体存储层中,我们将数据作为具有属性和链接的实体进行访问。我们使用实体存储 API,它提供了更丰富的数据查询选项和更高级别的抽象。
1. 依赖项
要开始使用实体存储,我们需要添加以下xodus-entity-store依赖项:
<dependency> <groupId>org.jetbrains.xodus</groupId> <artifactId>xodus-entity-store</artifactId> <version>2.0.1</version> </dependency>
|
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 }
|
我们已经创建了一个具有一些属性的TaskEntity。现在,我们将创建一个具有保存逻辑的存储库类:
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); } }
|
在这里,我们打开了一个PersistentEntityStore实例,然后启动了一个独占事务并创建了一个jetbrains.exodus.entitystore.Entity实例,将TaskEntity中的所有属性映射到它。EntityStore实体仅存在于事务中,因此我们需要将其映射到我们的DTO中以在存储库之外使用它。最后,我们从 save 方法返回了 EntityId。此 EntityId包含实体类型和唯一生成的 ID。
3. findOne()
现在,让我们将findOne()方法添加到我们的TaskEntityStoreRepository中:
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(); } }
|
在这里,我们在只读事务中访问实体并将其映射到我们的TaskEntity中。在映射方法中,我们实现以下逻辑:
private TaskEntity mapToTaskEntity(Entity entity) { return new TaskEntity(entity.getProperty("description").toString(), entity.getProperty("labels").toString()); }
|
我们获得了实体属性并使用它们创建了TaskEntity实例。
4. findAll()
让我们添加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; } }
|
我们可以看到,在 Environments 模拟中,实现过程要短得多。我们调用了实体存储事务getAll()方法,并将结果中的每个项目映射到TaskEntity中。
5.删除全部()
现在,让我们将deleteAll()方法添加到我们的TaskEntityStoreRepository中:
public void deleteAll() { try (PersistentEntityStore entityStore = openStore()) { entityStore.clear(); } }
|
在这里,我们必须调用PersistentEntityStore的clear()方法,所有项目将被删除。