Spring Data MongoDB - Relationships



Lets discuss a use case where a customer can purchase multiple products, based on review of a product. Also in this case, consider a customer has only one address to ship the product. Below is the ER diagram for this use case, we will discuss this relationship use case through Spring Data MongoDB.

Customer Product Review

Lets re−write the Customer document with other dependent class, which will be as follows.

Root Document Customer

import java.util.ArrayList;
import java.util.List;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
@Document
public class Customer {
   @Id
   private String id;
   private String name;
   @Indexed(unique = true)
   private String email;
   private Address address;
   private List<Product> products;
   public Customer() {
      this.products = new ArrayList<Product>();
   }
   public Customer(String name, String email, Address address, List<Produc
   t> products) {
      this.name = name;
      this.email = email;
      this.address = address;
      this.products = products;
   }
   public String getId() {
      return id;
   }
   public void setId(String id) {
      this.id = id;
   }
   public String getName() {
      return name;
   }
   public void setName(String name) {
      this.name = name;
   }
   public String getEmail() {
   return email;
   }
   public void setEmail(String email) {
      this.email = email;
   }
   public Address getAddress() {
      return address;
   }
   public void setAddress(Address address) {
      this.address = address;
   }
   public List<Product> getProducts() {
      return products;
   }
   public void setProducts(List<Product> products) {
      this.products = products;
   }
   @Override
   public String toString() {
      return "Customer [id=" + id + ", name=" + name + ", email=" + email
      + ", address=" + address + ", products="
      + products + "]";
   }
}

Address class

public class Address {
   private String city;
   private String country;
   private Integer zipCode;
   public Address() {
   }
   public Address(String city, String country, Integer zipCode) {
      this.city = city;
      this.country = country;
      this.zipCode = zipCode;
   }
   public String getCity() {
      return city;
   }
   public void setCity(String city) {
      this.city = city;
   }
   public String getCountry() {
      return country;
   }
   public void setCountry(String country) {
      this.country = country;
   }
   public Integer getZipCode() {
      return zipCode;
   }
   public void setZipCode(Integer zipCode) {
      this.zipCode = zipCode;
   }
   @Override
   public String toString() {
      return "Address [city=" + city + ", country=" + country + ", zipCod
      e=" + zipCode + "]";
   }
}

Product Document

import java.util.List;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Document
public class Product {
   @Id
   private String id;
   private String name;
   private Double price;
   private List<Review> reviews;
   public Product() {
   }
   public Product(String name, Double price, List<Review> reviews) {
      this.name = name;
      this.price = price;
      this.reviews = reviews;
   }
   public String getName() {
      return name;
   }
   public void setName(String name) {
      this.name = name;
   }
   public Double getPrice() {
      return price;
   }
   public void setPrice(Double price) {
      this.price = price;
   }
   public List<Review> getReviews() {
      return reviews;
   }
   public void setReviews(List<Review> reviews) {
      this.reviews = reviews;
   }
   public String getId() {
      return id;
   }
   public void setId(String id) {
      this.id = id;
   }
   @Override
   public String toString() {
      return "Product [id=" + id + ", name=" + name + ", price=" + price
      + ", reviews=" + reviews + "]";
   }
}

Review Class

public class Review {
   private String customerName;
   private Integer rating;
   private boolean approved;
   public Review() {
   }
   public Review(String customerName, Integer rating, boolean approved) {
      this.customerName = customerName;
      this.rating = rating;
      this.approved = approved;
   }
   public String getCustomerName() {
      return customerName;
   }
   public void setCustomerName(String customerName) {
      this.customerName = customerName;
   }
   public Integer getRating() {
      return rating;
   }
   public void setRating(Integer rating) {
      this.rating = rating;
   }
   public boolean isApproved() {
      return approved;
   }
   public void setApproved(boolean approved) {
      this.approved = approved;
   }
   @Override
   public String toString() {
      return "Review [ customerName=" + customerName + ", rating=" + rati
      ng + ", approved=" + approved + "]";
   }
}

Customer Repository

import java.util.Optional;
import org.springframework.data.mongodb.repository.MongoRepository;
import com.tutorialspoint.document.Customer;
public interface CustomerRepository extends MongoRepository<Customer, Strin
g> {
   Optional<Customer> findByEmail(String email);
}

Product Repository

import org.springframework.data.mongodb.repository.MongoRepository;
import com.tutorialspoint.document.Product;
public interface ProductRepository extends MongoRepository<Product, String>
{
   Product findByName(String string);
}

If you observe, we have not created a repository for Address and Reviews, because Spring Data MongoDb is a part of the Spring Data project and the complete Spring data project work on the aggregate root principle of DDD. Since the address is the part of the Customer document, and Review is part of the Product document, so we are required to create one repository per aggregate root. If we want we can skip the Product repository as well, and it still works.

In the above long list of documents, unlike our JPA based application we have not used any annotation for representing a relationship like @OneToOne or @OneToMany. Spring Data MongoDB doesnt entertain such annotations, and it understand the relationship based on context. We can find in the above documents that a customer can have an address and many products. Also a product could have many reviews. Lets try persisting some data and fetching it out. To do so we will be using CommandLineRunner.

import java.util.Arrays;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import com.tutorialspoint.document.Address;
import com.tutorialspoint.document.Customer;
import com.tutorialspoint.document.Product;
import com.tutorialspoint.document.Review;
import com.tutorialspoint.repository.CustomerRepository;
import com.tutorialspoint.repository.ProductRepository;
@SpringBootApplication
public class SpringDataMongoDbApplication {
   public static void main(String[] args) {
      SpringApplication.run(SpringDataMongoDbApplication.class, args);
   }
   @Bean
   CommandLineRunner commandLineRunner(CustomerRepository customerReposito
   ry, ProductRepository productRepository) {
      return new CommandLineRunner() {
         @Override
         public void run(String... args) throws Exception {
         // Persist a product with review
            Product product = new Product("IpHone", 60000.0d, Arrays.as
            List(new Review("asad", 4, true)));
            product = productRepository.save(product);
            // Persist a customer with address and product
            Customer customer = new Customer("Asad Ali", "asad@gmail.co
            m", new Address("Hyd", "India", 122001),
            Arrays.asList(product));
            customer = customerRepository.save(customer);
            // Fetch the customer
            System.out.println(customer);
         }
      };
   }
}

OUTPUT

Customer [id=5eb327768442b8385bd4bb1a, name=Asad Ali, email=asad@gmail.com,
address=Address [city=Hyd, country=India, zipCode=122001], products=[Product
[id=5eb327768442b8385bd4bb19, name=IpHone, price=60000.0, reviews=[Review [
customerName=asad, rating=4, approved=true]]]]]
Lets perform one more insert and retrieve operation to the above documents:
@Bean
CommandLineRunner commandLineRunner(CustomerRepository customerReposito
ry, ProductRepository productRepository) {
   return new CommandLineRunner() {
      @Override
      public void run(String... args) throws Exception {
         Product samsung = new Product("Samsung", 50000.0d, Arrays.a
         sList(new Review("ali", 5, true)));
         samsung = productRepository.save(samsung);
         //Fetch Old record with name Iphone
         Product product = productRepository.findByName("IpHone");
         // Save multiple two products for this user
         Customer customer = new Customer("Ali", "ali@gmail.com", ne
         w Address("Gurgaon", "India", 122018),
         Arrays.asList(samsung, product));
         customer = customerRepository.save(customer);
         System.out.println(customer);
         // Find by Email
         System.out.println(customerRepository.findByEmail("asad@gma
         il.com"));
      }
   };
}

OUTPUT

Customer [id=5eb32bd590ac0c3e200677ea, name=Ali, email=ali@gmail.com,
address=Address [city=Gurgaon, country=India, zipCode=122018], products=[Product
[id=5eb32bd490ac0c3e200677e9, name=Samsung, price=50000.0, reviews=[Review [
customerName=ali, rating=5, approved=true]]], Product [id=5eb327768442b8385bd4bb19,
name=IpHone, price=60000.0, reviews=[Review [ customerName=asad, rating=4,
approved=true]]]]]
Optional[Customer [id=5eb327768442b8385bd4bb1a, name=Asad Ali, email=asad@gmail.com,
address=Address [city=Hyd, country=India, zipCode=122001], products=[Product
[id=5eb327768442b8385bd4bb19, name=IpHone, price=60000.0, reviews=[Review [
customerName=asad, rating=4, approved=true]]]]]]
Advertisements