MongoDB – Model One-to-One, One-to-Many Relationships Embedded Documents | SpringBoot

With MongoDB, we can structure related data by embedded documents. In general, embedding gives a better performance for read operations. So in the tutorial, JavaSampleApproach will show you way to work with Embedded Documents using SpringBoot.

Related posts:
Spring JPA One to Many Relationship
SpringData MongoRepository
SpringData MongoDB GridFsTemplate to save, retrieve, delete binary files (Image, Text files)
Angular 4 + Spring Boot + MongoDB CRUD example

I. Technologies

– Java 1.8
– Maven 3.3.9
– Spring Tool Suite – Version 3.8.1.RELEASE
– Spring Boot: 1.5.6RELEASE
– MongoDB: v3.4.1

II. MongoDB – Embedded Documents

Embedded Documents are generally known as denormalized models. It is a way to structure related data in a single document. See below diagram:

mongodb embedded document - structure

In general, we decide to design data as embedded models in case: data contains one-to-one or one-to-many relationships between entities.

With embedding data model, in general, it gives us a better performance for read operations. And we can use a single atomic read/write database operation to request and retrieve or update related data.

However, a weakness point is embedding related data in documents may lead to situations where documents grow after creation.

III. Practice

In the tutorial, we use SpringToolSuite to create a SpringBoot project for MongoDB embedded documents:

mongodb embedded document - springboot project structure

Step to do:
– Create SpringBoot project
– Create Document models
– Create MongoRepository
– Run and check results
– Implement Client

1. Create SpringBoot project

Using Spring Tool Suite, create a SpringBoot project. Then open pom.xml file, add dependencies:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
    <version>1.5.4.RELEASE</version>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
</dependency>

2. Create Document models

Create 3 class models {Company, Product, Contact} with one-to-one relationship (between Company & Contact),
and one-to-many relationship (between Company & Product) in embedded data model structure.

Company


package com.javasampleapproach.datamongodb.model;

import java.util.List;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

@Document(collection="company")
public class Company {
	@Id
	private int id;
    private String name;
    
    private List products;
    
    private Contact contact;
    
    public void setId(int id){
    	this.id = id;
    }
    
    public int getId(){
    	return this.id;
    }
    
    public void setName(String name){
    	this.name = name;
    }
    
    public String getName(){
    	return this.name;
    }
    
    public void setProducts(List products){
    	this.products = products;
    }
    
    public List getProducts(){
    	return this.products;
    }
    
    public void setContact(Contact contact){
    	this.contact = contact;
    }
    
    public Contact getContact(){
    	return this.contact;
    }
    
    public Company(int id, String name, List products, Contact contact){
    	this.id = id;
    	this.name = name;
    	this.products = products;
    	this.contact = contact;
    }
    
    @Override
    public String toString() {
    	ObjectMapper mapper = new ObjectMapper();
    	
    	String jsonString = "";
		try {
			mapper.enable(SerializationFeature.INDENT_OUTPUT);
			jsonString = mapper.writeValueAsString(this);
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}
		
    	return jsonString;
    }
}

Contact:


package com.javasampleapproach.datamongodb.model;

public class Contact {
	private String address;
	private String phone;
	
	public Contact(String address, String phone){
		this.address = address;
		this.phone = phone;
	}
	
	public void setAddress(String address){
		this.address = address;
	}
	
	public String getAddress(){
		return this.address;
	}
	
	public void setPhone(String phone){
		this.phone = phone;
	}
	
	public String getPhone(){
		return this.phone;
	}
}

Product:


package com.javasampleapproach.datamongodb.model;

public class Product {
	private String code;
	private String name;
	private String details;
	
	public Product(String code, String name, String details){
		this.code = code;
		this.name = name;
		this.details = details;
	}
	
	public void setCode(String code){
		this.code = code;
	}
	
	public String getCode(){
		return this.code;
	}
	
	public void setName(String name){
		this.name = name;
	}
	
	public String getName(){
		return this.name;
	}
	
	public void setDetails(String details){
		this.details = details;
	}
	
	public String getDetails(){
		return this.details;
	}
}

3. Create MongoRepository

interface CompanyRepository extends MongoRepository


package com.javasampleapproach.datamongodb.repository;

import java.util.List;

import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;

import com.javasampleapproach.datamongodb.model.Company;

public interface CompanyRepository extends MongoRepository{
	List findByName(String name);
	
	@Query("{'contact.address': ?0}")
	List findByAddress(String address);
}

– Open application.properties, add configuration to connect with Mongo Server:


spring.data.mongodb.database=jsa_mongodb
spring.data.mongodb.port=27017

4. Implement Client

Use CommandLineRunner to implement a simple client to manipulate data from MongoDB via CompanyRepository:


package com.javasampleapproach.datamongodb;

import java.util.Arrays;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.javasampleapproach.datamongodb.model.Company;
import com.javasampleapproach.datamongodb.model.Contact;
import com.javasampleapproach.datamongodb.model.Product;
import com.javasampleapproach.datamongodb.repository.CompanyRepository;

@SpringBootApplication
public class SpringDataMongoDbApplication implements CommandLineRunner{

	@Autowired
	CompanyRepository companyRepo;
	
	@Override
	public void run(String... args) throws Exception {
		// clear database
		System.out.println("==========Delete all company entities==========");
		companyRepo.deleteAll();
		
		// save Documents to MongoDB
		System.out.println("==========Save list of company entities==========");
		companyRepo.save(Arrays.asList(
				// Apple company & products
				new Company(1, "Apple",
						
						        // list of products
								Arrays.asList(new Product("A-123", "Iphone 7", "Price: 649.00 USD & FREE shipping"),
													  new Product("A-456", "IPadPro", "Price: 417.67 USD & FREE shipping")),
								
								// contact
								new Contact("Cupertino, CA 95014", "1-408-996-1010")),
				
				// Samsung company & products
				new Company(2, "Samsung",
						
						       // list of products
						       Arrays.asList(new Product("S-012", "GalaxyJ7", "Price: 219.00 USD & FREE shipping"),
														new Product("S-456", "GalaxyTabA", "Price: 299.99 USD & FREE shipping")),
						       
						       // contact
						       new Contact("Seocho District, Seoul, South Korea", "+82-2-2053-3000"))));
		// initial List Companies variable
		List companies = null;
		
		// fetch all documents
		System.out.println("==========Fetch aLL companies:==========");
		companies = companyRepo.findAll();
		companies.forEach(System.out::println);
		
		// find Company by name
		System.out.println("==========Find a company by name:==========");
		companies = companyRepo.findByName("Samsung");
		companies.forEach(System.out::println);
		
		// find Company by address
		System.out.println("==========Find a company by address:==========");
		companies = companyRepo.findByAddress("Cupertino, CA 95014");
		companies.forEach(System.out::println);
	}
	
	public static void main(String[] args) {
		SpringApplication.run(SpringDataMongoDbApplication.class, args);
	}
}

5. Run and check results

Start MongoDB server by commandline: .\MongoDB\Server\3.4\bin>mongod.exe

-> Logs:


2017-08-18T16:52:34.225+0700 I CONTROL  [initandlisten]
2017-08-18T16:52:35.518+0700 I FTDC     [initandlisten] Initializing full-time diagnostic data capture with directory 'C:/data/db/diagnostic.data'
2017-08-18T16:52:35.519+0700 I NETWORK  [thread1] waiting for connections on port 27017

Build and Run the SpringBoot project with commandlines: mvn clean install, mvn spring-boot:run

-> Logs:


==========Delete all company entities==========
==========Save list of company entities==========
==========Fetch aLL companies:==========
{
  "id" : 1,
  "name" : "Apple",
  "products" : [ {
    "code" : "A-123",
    "name" : "Iphone 7",
    "details" : "Price: 649.00 USD & FREE shipping"
  }, {
    "code" : "A-456",
    "name" : "IPadPro",
    "details" : "Price: 417.67 USD & FREE shipping"
  } ],
  "contact" : {
    "address" : "Cupertino, CA 95014",
    "phone" : "1-408-996-1010"
  }
}
{
  "id" : 2,
  "name" : "Samsung",
  "products" : [ {
    "code" : "S-012",
    "name" : "GalaxyJ7",
    "details" : "Price: 219.00 USD & FREE shipping"
  }, {
    "code" : "S-456",
    "name" : "GalaxyTabA",
    "details" : "Price: 299.99 USD & FREE shipping"
  } ],
  "contact" : {
    "address" : "Seocho District, Seoul, South Korea",
    "phone" : "+82-2-2053-3000"
  }
}
==========Find a company by name:==========
{
  "id" : 2,
  "name" : "Samsung",
  "products" : [ {
    "code" : "S-012",
    "name" : "GalaxyJ7",
    "details" : "Price: 219.00 USD & FREE shipping"
  }, {
    "code" : "S-456",
    "name" : "GalaxyTabA",
    "details" : "Price: 299.99 USD & FREE shipping"
  } ],
  "contact" : {
    "address" : "Seocho District, Seoul, South Korea",
    "phone" : "+82-2-2053-3000"
  }
}
==========Find a company by address:==========
{
  "id" : 1,
  "name" : "Apple",
  "products" : [ {
    "code" : "A-123",
    "name" : "Iphone 7",
    "details" : "Price: 649.00 USD & FREE shipping"
  }, {
    "code" : "A-456",
    "name" : "IPadPro",
    "details" : "Price: 417.67 USD & FREE shipping"
  } ],
  "contact" : {
    "address" : "Cupertino, CA 95014",
    "phone" : "1-408-996-1010"
  }
}

Run MongoDB shell by commandline: .\MongoDB\Server\3.4\bin>mongo.exe:

-> Logs:


C:\Program Files\MongoDB\Server\3.4\bin>mongo.exe
MongoDB shell version v3.4.1
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.4.1
Server has startup warnings:
2017-08-18T16:52:34.224+0700 I CONTROL  [initandlisten]
2017-08-18T16:52:34.224+0700 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.
2017-08-18T16:52:34.225+0700 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.
2017-08-18T16:52:34.225+0700 I CONTROL  [initandlisten]
>

Results with MongoDB shell:

mongodb embedded document - mongodb shell results

show dbs is used to show all MongoDB databases. -> Having one database jsa_mongodb which is created by the SpringBoot MongoRepository application.
use jsa_mongodb is used to select the jsa_mongodb database to work.
show collections is used to show all collections in current database. -> We just have one collection: {company}
db.company.find() is used to query all documents of the collection company. -> We have 2 Company documents {Apple, Samsung}.

IV. Sourcecode

SpringMongoDbEmbeddedDocument



By grokonez | August 18, 2017.

Last updated on April 9, 2021.



Related Posts


9 thoughts on “MongoDB – Model One-to-One, One-to-Many Relationships Embedded Documents | SpringBoot”

  1. your demo example for one to many relation in mongodb works fine.but in company to address mapping only company collection data is inserted corresponding address collection is empty.

    1. More infomation for right understanding about MongoDB Emdedded model:

      – Emdedded model is the way we embed related data in a single structure or document.

      => So we just have one collection that stores document.
      In the case, We have only company collection. And address collection does not exist.

      Regards,

  2. Hey dude, really nice your demo!

    I just have a silly question,
    Introducing:

    I have two entities: Plant and Plantation. Plant is a document that contains all plants I have (Tomato, Potato, etc), and plantation is another document that contains a Plant and details about plantation (When I sown it).
    I know my thinking is still in ER DB but I cannot understand how I could reuse those plants inside my plantation without create a new/duplicated plant for every single plantation.

    I saw people using a plantId inside plantation and just keep the relationship like a string to another document. But in code perspective it is not nice, because when I am gonna work with my plantation object I will not have my plant as well, only plant ID.

    How could I do that?

    Thank you!

  3. Great example, Just a couple code correction.

    When writing the CompanyRepository.java the types should be specified otherwise the code launches an error:
    org.springframework.data.mapping.MappingException: Couldn’t find PersistentEntity for type class java.lang.Object!

    To avoid this the following addition should be made:
    Instead of: public interface CompanyRepository extends MongoRepository {
    use: public interface CompanyRepository extends MongoRepository {

    When implementing the command line Client (SpringDataMongoDbApplication.java) when saving the documents into the MongoDB there is a mistake generating an error.

    Instead of: companyRepo.save(Arrays.asList( ···
    It should be: companyRepo.saveAll(Arrays.asList(···

Got Something To Say:

Your email address will not be published. Required fields are marked *

*