Enterprise Java

Morphia – Java ODM for MongoDB

1. Introduction

This is an in-depth article on Morphia Java ODM for MongoDB example. Mongo Database is a no SQL database. It has capabilities such as query language to retrieve from the database. It also provides operational and administrative procedures. Morphia can be used as Object Document Mapper for MongoDB. This operates on top of Java Driver for MongoDB.

2. Morphia

2.1 Prerequisites

MongoDB needs to be installed for the MongoDB Upsert example.

2.2 Download

You can download the Mongo DB from the Mongo Database website for Linux, Windows, or macOS versions.

2.3 Setup

On macOS, you need to tap the formula repository of MongoDB. This repo needs to be added to the formula list. The command below adds the formula repository of MongoDB to the formula list:

Brew Tap Command

brew tap mongodb/brew

After setting the formula list, you can install the Mongo DB with the following command :

Brew Install command

brew install mongodb-community@4.0

2.4 MongoDB CommandLine

After installation, you can run MongoDB on the command line. To run MongoDB on the command line, the following command can be used:

Mongo Execution command

mongod --config /usr/local/etc/mongod.conf

The output of the executed command is shown below.

MongoDB Data Models - Command Line
Mongod Command Line

2.5 Mongo DB Operations

After starting the Mongod process, Mongo Shell can be invoked on the command line. Mongo shell can be run using the command below:

Mongo Shell command

 mongo

The output of the executed command is shown below.

MongoDB Data Models - Mongo Shell
Mongo Shell

2.5.1 Database Initialization

You can use database_name to create a database. This command will create a new database. If the database exists, it will start using the existing database. The command below is used to create “octopus” database:

Database creation command

 use octopus

The output of the executed command is shown below.

MongoDB Data Models - Create Database
Create Database

2.5.2 Drop Database

You can use the dropDatabase() command to drop the existing database. The command below is used to drop a database. This command will delete the database​. If used this command without db, then the default ‘test’ database is deleted. 

Delete Database command

 db.dropDatabase()

The output of the executed command is shown below.

MongoDB Data Models - Drop Database
Drop Database

2.5.3 What Is an ODM?

Object Document Mapper is used for mapping Java Plain Old Java Objects to MongoDB Collections. ODM provides a connection to the MongoDB database. This connection can be used for different database operations. DataStore class is provided for MongoDB client operations. You can pass MongoClient Instance and database name to get the active connection.

2.5.4  Dependencies

Dependencies are managed by maven. Morphia and MongoDb driver dependencies are added in pom.xml as shown below.

Dependencies

 
        &ltdependency&gt
            &ltgroupId&gtde.flapdoodle.embedmongo&lt/groupId&gt
            &ltartifactId&gtde.flapdoodle.embedmongo&lt/artifactId&gt
            &ltversion&gt${flapdoodle.version}&lt/version&gt
            &ltscope&gttest&lt/scope&gt
        &lt/dependency&gt
        &ltdependency&gt
            &ltgroupId&gtorg.mongodb&lt/groupId&gt
            &ltartifactId&gtmongo-java-driver&lt/artifactId&gt
            &ltversion&gt${mongo.version}&lt/version&gt
        &lt/dependency&gt
        &ltdependency&gt
            &ltgroupId&gtdev.morphia.morphia&lt/groupId&gt
            &ltartifactId&gtcore&lt/artifactId&gt
            &ltversion&gt${morphia.version}&lt/version&gt
        &lt/dependency&gt
        &ltdependency&gt
            &ltgroupId&gtorg.testcontainers&lt/groupId&gt
            &ltartifactId&gtmongodb&lt/artifactId&gt
            &ltversion&gt1.16.3&lt/version&gt
            &ltscope&gttest&lt/scope&gt
        &lt/dependency&gt

2.5.5 Entities (Simple, with relationships)

You can model relationships with Morphia using Referencing and Embedding. The product can be modeled to have a relationship with Company and other products created by the same company. You can look at the sample code below Product has a relationship with Writer and Company.

Product & Writer & Company relationships

package org.javacodegeeks.morphia.domain;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.Set;

import dev.morphia.annotations.Embedded;
import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Field;
import dev.morphia.annotations.Id;
import dev.morphia.annotations.Index;
import dev.morphia.annotations.IndexOptions;
import dev.morphia.annotations.Indexes;
import dev.morphia.annotations.Property;
import dev.morphia.annotations.Reference;
import dev.morphia.annotations.Validation;

@Entity("Products")
@Indexes({ @Index(fields = @Field("title"), options = @IndexOptions(name = "product_title")) })
@Validation("{ price : { $gt : 0 } }")
public class Product {
    @Id
    private String isbn;
    @Property
    private String title;
    private String writer;
    @Embedded
    private Company company;
    @Property("price")
    private double cost;
    @Reference
    private Set companionProducts;
    @Property
    private LocalDateTime publishDate;

    public Product() {

    }

    public Product(String isbn, String title, String writer, double cost, Company company) {
        this.isbn = isbn;
        this.title = title;
        this.writer = writer;
        this.cost = cost;
        this.company = company;
        this.companionProducts = new HashSet();
    }

 
    public String getIsbn() {
        return isbn;
    }

    public Product setIsbn(String isbn) {
        this.isbn = isbn;
        return this;
    }

    public String getTitle() {
        return title;
    }

    public Product setTitle(String title) {
        this.title = title;
        return this;
    }

    public String getWriter() {
        return writer;
    }

    public Product setWriter(String writer) {
        this.writer = writer;
        return this;
    }

    public Company getCompany() {
        return company;
    }

    public Product setCompany(Company company) {
        this.company = company;
        return this;
    }

    public double getCost() {
        return cost;
    }

    public Product setCost(double cost) {
        this.cost = cost;
        return this;
    }

    public LocalDateTime getPublishDate() {
        return publishDate;
    }

    public Product setPublishDate(LocalDateTime publishDate) {
        this.publishDate = publishDate;
        return this;
    }

    public Set getCompanionProducts() {
        return companionProducts;
    }

    public Product addCompanionProducts(Product product) {
        if (companionProducts != null)
            this.companionProducts.add(product);
        return this;
    }

    @Override
    public String toString() {
        return "Product [isbn=" + isbn + ", title=" + title + ", writer=" + writer + ", company=" + company + ", cost=" + cost + "]";
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((writer == null) ? 0 : writer.hashCode());
        long temp;
        temp = Double.doubleToLongBits(cost);
        result = prime * result + (int) (temp ^ (temp >>> 32));
        result = prime * result + ((isbn == null) ? 0 : isbn.hashCode());
        result = prime * result + ((company == null) ? 0 : company.hashCode());
        result = prime * result + ((title == null) ? 0 : title.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Product other = (Product) obj;
        if (writer == null) {
            if (other.writer != null)
                return false;
        } else if (!writer.equals(other.writer))
            return false;
        if (Double.doubleToLongBits(cost) != Double.doubleToLongBits(other.cost))
            return false;
        if (isbn == null) {
            if (other.isbn != null)
                return false;
        } else if (!isbn.equals(other.isbn))
            return false;
        if (company == null) {
            if (other.company != null)
                return false;
        } else if (!company.equals(other.company))
            return false;
        if (title == null) {
            if (other.title != null)
                return false;
        } else if (!title.equals(other.title))
            return false;
        return true;
    }

}

2.5.6 Basic Operations with Database

Let us look at the basic save operation and how Morphia can be used to save the products in a datastore. The sample code is shown below:

MongoDB Operations

package org.javacodegeeks.morphia;

import static dev.morphia.aggregation.Group.grouping;
import static dev.morphia.aggregation.Group.push;


import java.util.Iterator;
import java.util.List;

import org.bson.types.ObjectId;


import org.javacodegeeks.morphia.domain.Writer;
import org.javacodegeeks.morphia.domain.Product;
import org.javacodegeeks.morphia.domain.Company;
import com.mongodb.MongoClient;

import dev.morphia.Datastore;
import dev.morphia.Morphia;
import dev.morphia.query.Query;
import dev.morphia.query.UpdateOperations;


public class MorphiaExample {

    private static Datastore datastore;
    private static ObjectId id = new ObjectId();

    public static void main(String[] args)
	{
        Morphia morphia = new Morphia();
        morphia.mapPackage("org.javacodegeeks.morphia");
        datastore = morphia.createDatastore(new MongoClient(), "productshop");
        datastore.ensureIndexes();

        Company company = new Company(id, "Apress");
        Product product = new Product("23186", "Learning Java 8", "Tom Smith", 4.65, company);
        Product companionProduct = new Product("2975103", "Rust Companion", "Mike Douglas", 2.45, company);
        product.addCompanionProducts(companionProduct);
        datastore.save(companionProduct);
        datastore.save(product);

        List products = datastore.createQuery(Product.class)
            .field("title")
            .contains("Learning Java 8")
            .find()
            .toList();
        System.out.println(products.size());
        System.out.println(products.get(0));

        company = new Company(id, "Oreilly");
        product = new Product("231787", "Learning Java 9", "George Smith", 9.98, company);
        datastore.save(product);
        Query query = datastore.createQuery(Product.class)
            .field("title")
            .contains("Learning Java 9");
        UpdateOperations updates = datastore.createUpdateOperations(Product.class)
            .inc("price", 1);
        datastore.update(query, updates);
        products = datastore.createQuery(Product.class)
            .field("title")
            .contains("Learning Java 9")
            .find()
            .toList();
        System.out.println( products.get(0).getCost());

        company = new Company(id, "Manning");
        product = new Product("24176", "Learning Go", "John Smith", 6.43, company);
        datastore.save(product);
        query = datastore.createQuery(Product.class)
            .field("title")
            .contains("Learning Go");
        datastore.delete(query);
       products = datastore.createQuery(Product.class)
            .field("title")
            .contains("Learning Go")
            .find()
            .toList();
        System.out.println( products.size());

        company = new Company(id, "Hatcher");
        datastore.save(new Product("9781565927186", "Learning Dart", "Tom Smith", 3.21, company));
        datastore.save(new Product("9781449313142", "Learning Adobe", "Mark Smith", 6.32, company));
        datastore.save(new Product("9787564100476", "Learning Python 3", "Sandy Beger", 9.81, company));
        datastore.save(new Product("9781449368814", "Learning Scala 6", "Mark Sawyer", 8.72, company));
        datastore.save(new Product("9781784392338", "Learning Go 3", "John Sawyer", 6.43, company));

        Iterator authors = datastore.createAggregation(Product.class)
            .group("author", grouping("products", push("title")))
            .out(Writer.class);

        System.out.println(authors.hasNext());


        company = new Company(id, "Macmillan");
        product = new Product("654321", "Learning C++", "Kerngie Richhie", 4.53, company);
        datastore.save(product);
        products = datastore.createQuery(Product.class)
            .field("title")
            .contains("Learning C++")
            .project("title", true)
            .find()
            .toList();
        System.out.println(products.size());
        System.out.println( products.get(0)
            .getTitle());
        System.out.println(products.get(0)
            .getWriter());
    }
	
}

2.5.7 Aggregation

Aggregation is another operation that Morphia supports. Aggregation is a set of sequential operations in a pipeline to create aggregated output. You can use group operation as shown below in the sample code.

Aggregation

company = new Company(id, "Hatcher");
        datastore.save(new Product("9781565927186", "Learning Dart", "Tom Smith", 3.21, company));
        datastore.save(new Product("9781449313142", "Learning Adobe", "Mark Smith", 6.32, company));
        datastore.save(new Product("9787564100476", "Learning Python 3", "Sandy Beger", 9.81, company));
        datastore.save(new Product("9781449368814", "Learning Scala 6", "Mark Sawyer", 8.72, company));
        datastore.save(new Product("9781784392338", "Learning Go 3", "John Sawyer", 6.43, company));

        Iterator authors = datastore.createAggregation(Product.class)
            .group("author", grouping("products", push("title")))
            .out(Writer.class);

        System.out.println(authors.hasNext());

2.5.8 Projection

You can use projection to pick only a set of fields from the entity properties to query the MongoDB database. The sample code is shown below.

Projection

company = new Company(id, "Macmillan");
        product = new Product("654321", "Learning C++", "Kerngie Richhie", 4.53, company);
        datastore.save(product);
        products = datastore.createQuery(Product.class)
            .field("title")
            .contains("Learning C++")
            .project("title", true)
            .find()
            .toList();
        System.out.println(products.size());
        System.out.println( products.get(0)
            .getTitle());
        System.out.println(products.get(0)
            .getWriter());

2.5.9 Schema Validation

You can use Morphia for data validation rules of a collection. This can be done while data is being updated or inserted.

Schema Validation

@Entity("Products")
@Indexes({ @Index(fields = @Field("title"), options = @IndexOptions(name = "product_title")) })
@Validation("{ price : { $gt : 0 } }")
public class Product {
    @Id
    private String isbn;
    @Property
    private String title;
    private String writer;
    @Embedded
    private Company company;
    @Property("price")
    private double cost;
    @Reference
    private Set companionProducts;
    @Property
    private LocalDateTime publishDate;

    public Product() {

    }

3. Download the Source Code

Download
You can download the full source code of this example here: Morphia – Java ODM for MongoDB

Bhagvan Kommadi

Bhagvan Kommadi is the Founder of Architect Corner & has around 20 years’ experience in the industry, ranging from large scale enterprise development to helping incubate software product start-ups. He has done Masters in Industrial Systems Engineering at Georgia Institute of Technology (1997) and Bachelors in Aerospace Engineering from Indian Institute of Technology, Madras (1993). He is member of IFX forum,Oracle JCP and participant in Java Community Process. He founded Quantica Computacao, the first quantum computing startup in India. Markets and Markets have positioned Quantica Computacao in ‘Emerging Companies’ section of Quantum Computing quadrants. Bhagvan has engineered and developed simulators and tools in the area of quantum technology using IBM Q, Microsoft Q# and Google QScript. He has reviewed the Manning book titled : "Machine Learning with TensorFlow”. He is also the author of Packt Publishing book - "Hands-On Data Structures and Algorithms with Go".He is member of IFX forum,Oracle JCP and participant in Java Community Process. He is member of the MIT Technology Review Global Panel.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button