What we will do in this text?
- Run MongoDB using docker;
- Create a CRUD API using Spring Boot and MongoDB;
Now, let’s run MongoDB using Docker
Create a docker-compose.yml file:
version: '3.1'
services:
mongo:
image: mongo
restart: always
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: example
Run with this command:
docker-compose up
Using MongoDB Compass
If you don’t have this installed, it’s a good time to install it.
Use this string connection: mongodb://root:example@localhost:27017/ to connect.
After, create a database and a collection:
Importing data
Here https://www.kaggle.com/datasets we can get a database to import and use in our study environment.
I will choose this database https://www.kaggle.com/datasets/deepcontractor/marvel-comic-books
Download it.
Now, import the file:
It’s done:
The documents are available:
Now, let’s make some code
At first, we need these two dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Now, I mapped the documents in a java class:
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
@Document("rice_production")
public class RiceProductionData {
@Id
private String id;
@Field("Area")
private String area;
@Field("Year")
private String year;
@Field("Unit")
private String unit;
@Field("Value")
private String value;
@Field("Flag")
private String flag;
@Field("Flag Description")
private String flagDescription;
public RiceProductionData(String area, String year, String unit, String value, String flag, String flagDescription) {
this.area = area;
this.year = year;
this.unit = unit;
this.value = value;
this.flag = flag;
this.flagDescription = flagDescription;
}
public static RiceProductionData update(final String id, final RiceProductionData riceProductionData) {
final RiceProductionData riceProductionDataUpdated = new RiceProductionData();
riceProductionDataUpdated.setId(id);
riceProductionDataUpdated.setArea(riceProductionData.getArea() == null? null : riceProductionData.getArea());
riceProductionDataUpdated.setFlag(riceProductionData.getFlag() == null? null : riceProductionData.getFlag());
riceProductionDataUpdated.setYear(riceProductionData.getYear() == null? null : riceProductionData.getYear());
riceProductionDataUpdated.setUnit(riceProductionData.getUnit() == null? null : riceProductionData.getUnit());
riceProductionDataUpdated.setValue(riceProductionData.getValue() == null? null : riceProductionData.getValue());
riceProductionDataUpdated.setFlagDescription(riceProductionData.getFlagDescription() == null? null : riceProductionData.getFlagDescription());
return riceProductionDataUpdated;
}
public String getId() {
return id;
}
public String getArea() {
return area;
}
public String getYear() {
return year;
}
public String getUnit() {
return unit;
}
public String getValue() {
return value;
}
public String getFlag() {
return flag;
}
public String getFlagDescription() {
return flagDescription;
}
}
The method called “update” will auxiliary in an update.
And then created a repository interface:
import com.example.demo.adapters.database.document.RiceProductionData;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
import java.util.UUID;
@Repository
public interface SpringDataRiceProductionRepository extends MongoRepository<RiceProductionData, UUID> {
List<RiceProductionData> findByArea(final String countryName);
}
The method findByArea is an example of using a property to filter.
And put the annotation @EnableMongoRepositories in the main class:
import com.example.demo.adapters.database.repository.SpringDataRiceProductionRepository;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
@EnableMongoRepositories(basePackageClasses = SpringDataRiceProductionRepository.class)
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
Now, I will create a controller class:
import com.example.demo.adapters.database.document.RiceProductionData;
import com.example.demo.adapters.database.repository.SpringDataRiceProductionRepository;
import org.springframework.data.domain.Example;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/rice-production")
public class RiceProductionController {
private final SpringDataRiceProductionRepository springDataRiceProductionRepository;
public RiceProductionController(final SpringDataRiceProductionRepository springDataRiceProductionRepository) {
this.springDataRiceProductionRepository = springDataRiceProductionRepository;
}
@PostMapping
public ResponseEntity<?> create(@RequestBody final RiceProductionData riceProductionData) {
return ResponseEntity.ok(springDataRiceProductionRepository.save(riceProductionData));
}
@PutMapping("/{id}")
public ResponseEntity<?> update(@PathVariable final String id, @RequestBody final RiceProductionData riceProductionData) {
var response = springDataRiceProductionRepository.findById(id);
if(response.isEmpty()) {
return ResponseEntity.notFound().build();
}
var responseUpdated = RiceProductionData.update(id, riceProductionData);
return ResponseEntity.ok(springDataRiceProductionRepository.save(responseUpdated));
}
@GetMapping
public ResponseEntity<?> listRiceproduction() {
return ResponseEntity.ok(springDataRiceProductionRepository.findAll());
}
@GetMapping("/area/{countryName}")
public ResponseEntity<?> findByArea(@PathVariable final String countryName) {
return ResponseEntity.ok(springDataRiceProductionRepository.findByArea(countryName));
}
@GetMapping("/{id}")
public ResponseEntity<?> findById(@PathVariable final String id) {
return ResponseEntity.ok(springDataRiceProductionRepository.findById(id));
}
@DeleteMapping("/{id}")
public ResponseEntity<?> deleteById(@PathVariable final String id) {
springDataRiceProductionRepository.deleteById(id);
return ResponseEntity.ok().build();
}
}
You are thinking why there is a repository here, huh? Relax, it’s only a test.
Tests
We will execute some curls:
POST
curl --location --request POST 'http://localhost:8080/rice-production' \
--header 'Content-Type: application/json' \
--data-raw '{
"area": "Brazil",
"year": "1900",
"unit": "tonnes",
"value": "50000",
"flag": "A",
"flagDescription": "Official figure"
}'
Response:
{
"id": "640e0cc1ee5b2a06c23630df",
"area": "Brazil",
"year": "1900",
"unit": "tonnes",
"value": "50000",
"flag": "A",
"flagDescription": "Official figure"
}
GET by area (or country)
curl --location --request GET 'http://localhost:8080/rice-production/area/Brazil'
Response:
[
{
"id": "640df82a6a39a9848c47feda",
"area": "Brazil",
"year": "1961",
"unit": "tonnes",
"value": "5392477",
"flag": "A",
"flagDescription": "Official figure"
},
{
"id": "640df82a6a39a9848c47fedb",
"area": "Brazil",
"year": "1962",
"unit": "tonnes",
"value": "5556834",
"flag": "A",
"flagDescription": "Official figure"
},
{
"id": "640df82a6a39a9848c47fedc",
"area": "Brazil",
"year": "1963",
"unit": "tonnes",
"value": "5740065",
"flag": "A",
"flagDescription": "Official figure"
},
{
"id": "640df82a6a39a9848c47fedd",
"area": "Brazil",
"year": "1964",
"unit": "tonnes",
"value": "6344931",
"flag": "A",
"flagDescription": "Official figure"
},
{
"id": "640df82a6a39a9848c47fede",
"area": "Brazil",
"year": "1965",
"unit": "tonnes",
"value": "7579649",
"flag": "A",
"flagDescription": "Official figure"
},
{
"id": "640df82a6a39a9848c47fedf",
"area": "Brazil",
"year": "1966",
"unit": "tonnes",
"value": "5801814",
"flag": "A",
"flagDescription": "Official figure"
},
{
"id": "640df82a6a39a9848c47fee0",
"area": "Brazil",
"year": "1967",
"unit": "tonnes",
"value": "6791990",
"flag": "A",
"flagDescription": "Official figure"
},
{
"id": "640df82a6a39a9848c47fee1",
"area": "Brazil",
"year": "1968",
"unit": "tonnes",
"value": "6652388",
"flag": "A",
"flagDescription": "Official figure"
},
{
"id": "640df82a6a39a9848c47fee2",
"area": "Brazil",
"year": "1969",
"unit": "tonnes",
"value": "6394285",
"flag": "A",
"flagDescription": "Official figure"
},
{
"id": "640df82a6a39a9848c47fee3",
"area": "Brazil",
"year": "1970",
"unit": "tonnes",
"value": "7553083",
"flag": "A",
"flagDescription": "Official figure"
},
{
"id": "640df82a6a39a9848c47fee4",
"area": "Brazil",
"year": "1971",
"unit": "tonnes",
"value": "6593179",
"flag": "A",
"flagDescription": "Official figure"
},
//...
{
"id": "640df82a6a39a9848c47ff16",
"area": "Brazil",
"year": "2021",
"unit": "tonnes",
"value": "11660603",
"flag": "A",
"flagDescription": "Official figure"
}
]
GET by id
curl --location --request GET 'http://localhost:8080/rice-production/640df82a6a39a9848c47feda'
Response:
{
"id": "640df82a6a39a9848c47feda",
"area": "Brazil",
"year": "1961",
"unit": "tonnes",
"value": "5392477",
"flag": "A",
"flagDescription": "Official figure"
}
DELETE by id
curl --location --request DELETE 'http://localhost:8080/rice-production/640df82a6a39a9848c47feda'
PUT
First, I choose a document:
{
"id": "640df82a6a39a9848c47fedc",
"area": "Brazil",
"year": "1963",
"unit": "tonnes",
"value": "5740065",
"flag": "A",
"flagDescription": "Official figure"
}
Then I made this curl changing the year to 1903:
curl --location --request PUT 'http://localhost:8080/rice-production/640df82a6a39a9848c47fedc' \
--header 'Content-Type: application/json' \
--data-raw '{
"area": "Brazil",
"year": "1903",
"unit": "tonnes",
"value": "5740065",
"flag": "A",
"flagDescription": "Official figure"
}'
And executing a GET by id again, the result is:
{
"id": "640df82a6a39a9848c47fedc",
"area": "Brazil",
"year": "1903",
"unit": "tonnes",
"value": "5740065",
"flag": "A",
"flagDescription": "Official figure"
}
So far so good until here, right?
This code is here: https://github.com/mmarcosab/springboot-mongo-demo
That’s it for today.