Achieving Inheritance in NoSQL Databases With Java Using Eclipse JNoSQL

NoSQL databases provide a flexible and scalable option for storing and retrieving data in database management. However, they can need help with object-oriented programming paradigms, such as inheritance, which is a fundamental concept in languages like Java. This article explores the impedance mismatch when dealing with inheritance in NoSQL databases.

The Inheritance Challenge in NoSQL Databases

The term “impedance mismatch” refers to the disconnect between the object-oriented world of programming languages like Java and NoSQL databases’ tabular, document-oriented, or graph-based structures. One area where this mismatch is particularly evident is in handling inheritance.

In Java, inheritance allows you to create a hierarchy of classes, where a subclass inherits properties and behaviors from its parent class. This concept is deeply ingrained in Java programming and is often used to model real-world relationships. However, NoSQL databases have no joins, and the inheritance structure needs to be handled differently.

Mapping mismatch graphic

Jakarta Persistence (JPA) and Inheritance Strategies

Before diving into more advanced solutions, it’s worth mentioning that there are strategies to simulate inheritance in relational databases in the world of Jakarta Persistence (formerly known as JPA). These strategies include:

  1. JOINED inheritance strategy: In this approach, fields specific to a subclass are mapped to a separate table from the fields common to the parent class. A join operation is performed to instantiate the subclass when needed.
  2. SINGLE_TABLE inheritance strategy: This strategy uses a single table representing the entire class hierarchy. Discriminator columns are used to differentiate between different subclasses.
  3. TABLE_PER_CLASS inheritance strategy: Each concrete entity class in the hierarchy corresponds to its table in the database.

These strategies work well in relational databases but are not directly applicable to NoSQL databases, primarily because NoSQL databases do not support traditional joins.

Live Code Session: Java SE, Eclipse JNoSQL, and MongoDB

In this live code session, we will create a Java SE project using MongoDB as our NoSQL database. We’ll focus on managing game characters, specifically Mario and Sonic characters, using Eclipse JNoSQL. You can run MongoDB locally using Docker or in the cloud with MongoDB Atlas. We’ll start with the database setup and then proceed to the Java code implementation.

Setting Up MongoDB Locally

To run MongoDB locally, you can use Docker with the following command:

Shell
 
docker run -d --name mongodb-instance -p 27017:27017 mongo


Alternatively, you can choose to execute it in the cloud by following the instructions provided by MongoDB Atlas.

With the MongoDB database up and running, let’s create our Java project.

Creating the Java Project

We’ll create a Java SE project using Maven and the maven-archetype-quickstart archetype. This project will utilize the following technologies and dependencies:

Maven Dependencies

Add the following dependencies to your project’s pom.xml file:

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>


Make sure to replace ${jnosql.version} with the appropriate version of Eclipse JNoSQL you intend to use.

In the next section, we will proceed with implementing our Java code.

Implementing Our Java Code

Our GameCharacter class will serve as the parent class for all game characters and will hold the common attributes shared among them. We’ll use inheritance and discriminator columns to distinguish between Sonic’s and Mario’s characters. Here’s the initial definition of the GameCharacter class:

Java
 
@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();

}


In this code:

The GameCharacter class has a unique identifier (id), attributes for character name (character) and game name (game), and an abstract method getType(), which its subclasses will implement to specify the character type.

Specialization Classes: Sonic and Mario

Now, let’s create the specialization classes for Sonic and Mario entities. These classes will extend the GameCharacter class and provide additional attributes specific to each character type. We’ll use @DiscriminatorValue to define the values the “type” discriminator column can take for each subclass.

Java
 
@Entity
@DiscriminatorValue("SONIC")
public class Sonic extends GameCharacter {

    @Column
    private String zone;

    @Override
    public GameType getType() {
        return GameType.SONIC;
    }
}


In the Sonic class:

Java
 
@Entity
@DiscriminatorValue("MARIO")
public class Mario extends GameCharacter {

    @Column
    private String locations;

    @Override
    public GameType getType() {
        return GameType.MARIO;
    }
}


Similarly, in the Mario class:

With this modeling approach, you can easily distinguish between Sonic and Mario characters in your MongoDB database using the discriminator column “type.”

We will create our first database integration with MongoDB using Eclipse JNoSQL. To simplify, we will generate data using the Data Faker library. Our Java application will insert Mario and Sonic characters into the database and perform basic operations.

Application Code

Here’s the main application code that generates and inserts data into the MongoDB database:

Java
 
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);
        }
    }
}


In this code:

Resulting Database Structure

As a result of running this code, you will see data in your MongoDB database similar to the following structure:

JSON
 
[
  {
    "_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"
  }
]


As shown in the database structure, each document contains a unique identifier (_id), character name (character), game name (game), and a discriminator column type to differentiate between Mario and Sonic characters. You will see more characters in your MongoDB database depending on your generated data.

This integration demonstrates how to insert, count, and retrieve game characters using Eclipse JNoSQL and MongoDB. You can extend and enhance this application to manage and manipulate your game character data as needed.

We will create repositories for managing game characters using Eclipse JNoSQL. We will have a Console repository for general game characters and a SonicRepository specifically for Sonic characters. These repositories will allow us to interact with the database and perform various operations easily.

Let’s define the repositories for our game characters.

Console Repository

Java
 
@Repository
public interface Console extends PageableRepository<GameCharacter, UUID> {
}


The Console repository extends PageableRepository and is used for general game characters. It provides common CRUD operations and pagination support.

Sonic Repository

Java
 
@Repository
public interface SonicRepository extends PageableRepository<Sonic, UUID> {
}


The SonicRepository extends PageableRepository but is specifically designed for Sonic characters. It inherits common CRUD operations and pagination from the parent repository.

Main Application Code

Now, let’s modify our main application code to use these repositories.

For Console Repository

Java
 
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);
}


In this code, we use the Console repository to save both Mario and Sonic characters, demonstrating its ability to manage general game characters.

For Sonic Repository

Java
 
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);
}


This code uses the SonicRepository to save Sonic characters specifically. It showcases how to work with a repository dedicated to a particular character type.

With these repositories, you can easily manage, query, and filter game characters based on their type, simplifying the code and making it more organized.

Conclusion

In this article, we explored the seamless integration of MongoDB with Java using the Eclipse JNoSQL framework for efficient game character management. We delved into the intricacies of modeling game characters, addressing challenges related to inheritance in NoSQL databases while maintaining compatibility with Java's object-oriented principles. By employing discriminator columns, we could categorize characters and store them within the MongoDB database, creating a well-structured and extensible solution.

Through our Java application, we demonstrated how to generate sample game character data using the Data Faker library and efficiently insert it into MongoDB. We performed essential operations, such as counting the number of game characters and retrieving specific character types. Moreover, we introduced the concept of repositories in Eclipse JNoSQL, showcasing their value in simplifying data management and enabling focused queries based on character types. This article provides a solid foundation for harnessing the power of Eclipse JNoSQL and MongoDB to streamline NoSQL database interactions in Java applications, making it easier to manage and manipulate diverse data sets.

 

 

 

 

Top