NoSQL 数据库为在数据库管理中存储和检索数据提供了灵活且可扩展的选项。但是,他们可能需要面向对象编程范式的帮助,例如继承,这是 Java 等语言中的基本概念。本文探讨了在处理 NoSQL 数据库中的继承时的阻抗不匹配问题。
NoSQL 数据库中的继承挑战
术语“阻抗不匹配”是指面向对象的编程语言(如 Java 和 NoSQL 数据库的表格、面向文档或基于图形的结构)之间的脱节。这种不匹配的一个特别明显的领域是处理继承。
在 Java 中,继承允许您创建类的层次结构,其中子类从其父类继承属性和行为。这个概念在 Java 编程中根深蒂固,通常用于对现实世界的关系进行建模。但是,NoSQL 数据库没有联接,继承结构需要以不同的方式处理。
雅加达持久性 (JPA) 和继承策略
在深入研究更高级的解决方案之前,值得一提的是,在 Jakarta Persistence(以前称为 JPA)的世界里,有一些策略可以模拟关系数据库中的继承。这些策略包括:
JOINED 继承策略:在此方法中,特定于子类的字段映射到与父类共有的字段不同的表。在需要时执行联接操作以实例化子类。
SINGLE_TABLE继承策略:此策略使用表示整个类层次结构的单个表。鉴别器列用于区分不同的子类。
TABLE_PER_CLASS继承策略:层次结构中的每个具体实体类都对应于其在数据库中的表。
这些策略在关系数据库中效果很好,但不能直接应用于 NoSQL 数据库,主要是因为 NoSQL 数据库不支持传统的连接。
实时代码会话:Java SE、Eclipse JNoSQL 和 MongoDB
在这个实时代码会话中,我们将创建一个使用 MongoDB 作为 NoSQL 数据库的 Java SE 项目。我们将专注于使用 Eclipse JNoSQL 管理游戏角色,特别是马里奥和索尼克角色。您可以使用 Docker 在本地运行 MongoDB,也可以使用 AtlasDB 在云中运行 MongoDB。我们将从数据库设置开始,然后继续进行 Java 代码实现。
在本地设置 MongoDB
若要在本地运行 MongoDB,可以通过以下命令使用 Docker:
docker run -d --name mongodb-instance -p 27017:27017 mongo
或者,您可以按照 MongoDB AtlasDB 提供的说明选择在云中执行它。
启动并运行 MongoDB 数据库后,让我们创建 Java 项目。
创建 Java 项目
我们将使用 Maven 和 maven-archetype-quickstart 原型创建一个 Java SE 项目。该项目将利用以下技术和依赖项:
雅加达CDI
雅加达 JSONP
Eclipse MicroProfile
Eclipse JNoSQL 数据库
Maven 依赖项
将以下依赖项添加到项目的 pom.xml 文件:
dependencies>
<dependency>
<groupId>org.jboss.weld.se</groupId>
<artifactId>weld-se-shaded</artifactId>
<version>${weld.se.core.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.eclipse</groupId>
<artifactId>yasson</artifactId>
<version>3.0.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.smallrye.config</groupId>
<artifactId>smallrye-config-core</artifactId>
<version>3.2.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.eclipse.microprofile.config</groupId>
<artifactId>microprofile-config-api</artifactId>
<version>3.0.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jnosql.databases</groupId>
<artifactId>jnosql-mongodb</artifactId>
<version>${jnosql.version}</version>
</dependency>
<dependency>
<groupId>net.datafaker</groupId>
<artifactId>datafaker</artifactId>
<version>2.0.2</version>
</dependency>
</dependencies>
请确保替换为您打算使用的 Eclipse JNoSQL 的适当版本。${jnosql.version}
在下一节中,我们将继续实现我们的 Java 代码。
实现我们的 Java 代码
我们的类将作为所有游戏角色的父类,并将保存它们之间共享的共同属性。我们将使用继承和鉴别器列来区分 Sonic 和 Mario 的角色。下面是该类的初始定义:GameCharacterGameCharacter
@Entity
@DiscriminatorColumn("type")
@Inheritance
public abstract class GameCharacter {
@Id
@Convert(UUIDConverter.class)
protected UUID id;
@Column
protected String character;
@Column
protected String game;
public abstract GameType getType();
}
在此代码中:
我们对类进行注释,以指示它是 MongoDB 数据库中的持久实体。@Entity
我们用于指定名为“type”的鉴别器列将用于区分子类。@DiscriminatorColumn(“type”)
@Inheritance指示此类是继承层次结构的一部分。
该类有一个唯一标识符 ()、角色名称 () 和游戏名称 () 的属性,以及一个抽象方法,其子类将实现该方法以指定角色类型。GameCharacteridcharactergamegetType()
专长课程:索尼克和马里奥
现在,让我们为 Sonic 和 Mario 实体创建专业化类。这些类将扩展 GameCharacter 类,并提供特定于每个角色类型的其他属性。我们将用于定义鉴别器列可以为每个子类获取的值。@DiscriminatorValue“type”
@Entity
@DiscriminatorValue("SONIC")
public class Sonic extends GameCharacter {
@Column
private String zone;
@Override
public GameType getType() {
return GameType.SONIC;
}
}
在 Sonic 类中:
我们用注释它来表示它是一个持久性实体。@Entity
@DiscriminatorValue(“SONIC”)指定鉴别器列将具有 Sonic 实体的值。“type”“SONIC”
我们为 Sonic 角色添加了特定于区域的属性。
该方法返回 ,指示这是一个 Sonic 字符。getType()GameType.SONIC
@Entity
@DiscriminatorValue("MARIO")
public class Mario extends GameCharacter {
@Column
private String locations;
@Override
public GameType getType() {
return GameType.MARIO;
}
}
同样,在马里奥类中:
我们用注释它来表示它是一个持久性实体。@Entity
@DiscriminatorValue(“MARIO”)指定“type”鉴别器列将具有 Mario 实体的值。“MARIO”
我们添加了一个特定于马里奥角色的属性。locations
该方法返回 ,指示这是一个马里奥角色。getType()GameType.MARIO
通过这种建模方法,您可以使用鉴别器列“type”轻松区分 MongoDB 数据库中的 Sonic 和 Mario 角色。
我们将使用 Eclipse JNoSQL 创建与 MongoDB 的第一个数据库集成。为简化起见,我们将使用 Data Faker 库生成数据。我们的 Java 应用程序会将 Mario 和 Sonic 角色插入数据库并执行基本操作。
应用程序代码
下面是生成数据并将其插入 MongoDB 数据库的主要应用程序代码:
public class App {
public static void main(String[] args) {
try (SeContainer container = SeContainerInitializer.newInstance().initialize()) {
DocumentTemplate template = container.select(DocumentTemplate.class).get();
DataFaker faker = new DataFaker();
Mario mario = Mario.of(faker.generateMarioData());
Sonic sonic = Sonic.of(faker.generateSonicData());
// Insert Mario and Sonic characters into the database
template.insert(List.of(mario, sonic));
// Count the total number of GameCharacter documents
long count = template.count(GameCharacter.class);
System.out.println("Total of GameCharacter: " + count);
// Find all Mario characters in the database
List<Mario> marioCharacters = template.select(Mario.class).getResultList();
System.out.println("Find all Mario characters: " + marioCharacters);
// Find all Sonic characters in the database
List<Sonic> sonicCharacters = template.select(Sonic.class).getResultList();
System.out.println("Find all Sonic characters: " + sonicCharacters);
}
}
}
在此代码中:
我们使用 来管理我们的 CDI 容器并初始化 from Eclipse JNoSQL。SeContainerDocumentTemplate
我们使用类生成的数据创建马里奥和索尼克角色的实例。DataFaker
我们使用该方法将这些字符插入到 MongoDB 数据库中。template.insert()
我们计算数据库中的文档总数。GameCharacter
我们从数据库中检索并显示所有马里奥和索尼克角色。
生成的数据库结构
运行此代码后,您将在 MongoDB 数据库中看到类似于以下结构的数据:
[
{
"_id": "39b8901c-669c-49db-ac42-c1cabdcbb6ed",
"character": "Bowser",
"game": "Super Mario Bros.",
"locations": "Mount Volbono",
"type": "MARIO"
},
{
"_id": "f60e1ada-bfd9-4da7-8228-6a7f870e3dc8",
"character": "Perfect Chaos",
"game": "Sonic Rivals 2",
"type": "SONIC",
"zone": "Emerald Hill Zone"
}
]
如数据库结构所示,每个文档都包含唯一标识符 ()、角色名称 ()、游戏名称 () 和一个鉴别器列,用于区分马里奥和索尼克角色。您将在 MongoDB 数据库中看到更多字符,具体取决于您生成的数据。_idcharactergametype
此集成演示了如何使用 Eclipse JNoSQL 和 MongoDB 插入、计数和检索游戏角色。您可以扩展和增强此应用程序,以根据需要管理和操作您的游戏角色数据。
我们将创建用于使用 Eclipse JNoSQL 管理游戏角色的存储库。我们将有一个用于一般游戏角色的控制台存储库和一个专门用于 Sonic 角色的存储库。这些存储库将使我们能够与数据库进行交互并轻松执行各种操作。SonicRepository
让我们为游戏角色定义仓库。
控制台存储库
@Repository
public interface Console extends PageableRepository<GameCharacter, UUID> {
}
该存储库扩展并用于一般游戏角色。它提供常见的 CRUD 操作和分页支持。ConsolePageableRepository
Sonic 存储库
@Repository
public interface SonicRepository extends PageableRepository<Sonic, UUID> {
}
扩展但专为 Sonic 角色设计。它从父存储库继承常见的 CRUD 操作和分页。SonicRepositoryPageableRepository
主要应用程序代码
现在,让我们修改主应用程序代码以使用这些存储库。
对于控制台存储库
public static void main(String[] args) {
Faker faker = new Faker();
try (SeContainer container = SeContainerInitializer.newInstance().initialize()) {
Console repository = container.select(Console.class).get();
for (int index = 0; index < 5; index++) {
Mario mario = Mario.of(faker);
Sonic sonic = Sonic.of(faker);
repository.saveAll(List.of(mario, sonic));
}
long count = repository.count();
System.out.println("Total of GameCharacter: " + count);
System.out.println("Find all game characters: " + repository.findAll().toList());
}
System.exit(0);
}
在此代码中,我们使用控制台存储库来保存马里奥和索尼克角色,演示其管理一般游戏角色的能力。
对于 Sonic 存储库
public static void main(String[] args) {
Faker faker = new Faker();
try (SeContainer container = SeContainerInitializer.newInstance().initialize()) {
SonicRepository repository = container.select(SonicRepository.class).get();
for (int index = 0; index < 5; index++) {
Sonic sonic = Sonic.of(faker);
repository.save(sonic);
}
long count = repository.count();
System.out.println("Total of Sonic characters: " + count);
System.out.println("Find all Sonic characters: " + repository.findAll().toList());
}
System.exit(0);
}
此代码专门使用 the 来保存 Sonic 字符。它展示了如何使用专用于特定字符类型的存储库。SonicRepository
借助这些存储库,您可以根据游戏角色的类型轻松管理、查询和过滤游戏角色,从而简化代码并使其更有条理。