-
Notifications
You must be signed in to change notification settings - Fork 0
Comprehensive Guide
This guide will walk you through setting up an empty Spring Boot 3 project with MongoDB connectivity, using Java 17, Gradle 8, and Lombok. I'll provide all the necessary code and configuration files.
First, let's create the basic project structure:
my-spring-project/
├── build.gradle
├── settings.gradle
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ └── demo/
│ │ │ ├── Application.java
│ │ │ ├── controller/
│ │ │ │ └── ProductController.java
│ │ │ ├── model/
│ │ │ │ └── Product.java
│ │ │ ├── repository/
│ │ │ │ └── ProductRepository.java
│ │ │ └── service/
│ │ │ └── ProductService.java
│ │ └── resources/
│ │ └── application.yaml
│ └── test/
│ └── java/
│ └── com/
│ └── example/
│ └── demo/
Create settings.gradle
:
rootProject.name = 'my-spring-project'
Create build.gradle
:
plugins {
id 'org.springframework.boot' version '3.2.0'
id 'io.spring.dependency-management' version '1.1.4'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
java {
sourceCompatibility = '17'
}
repositories {
mavenCentral()
}
dependencies {
// Spring Boot Starters
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
// Lombok
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
// Test dependencies
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
Create application.yaml
:
spring:
data:
mongodb:
host: localhost
port: 27017
database: mydatabase
# If authentication is needed (not required for local development):
# username: myuser
# password: mypassword
server:
port: 8080
logging:
level:
org:
springframework:
data:
mongodb:
core:
MongoTemplate: DEBUG
Create src/main/java/com/example/demo/Application.java
:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Create src/main/java/com/example/demo/model/Product.java
:
package com.example.demo.model;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
@Document(collection = "products")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product {
@Id
private String id;
private String name;
private String description;
private BigDecimal price;
}
Create src/main/java/com/example/demo/repository/ProductRepository.java
:
package com.example.demo.repository;
import com.example.demo.model.Product;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface ProductRepository extends MongoRepository<Product, String> {
// Custom query methods
List<Product> findByNameContainingIgnoreCase(String name);
List<Product> findByPriceLessThanEqual(BigDecimal maxPrice);
}
Create src/main/java/com/example/demo/service/ProductService.java
:
package com.example.demo.service;
import com.example.demo.model.Product;
import com.example.demo.repository.ProductRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
@Service
public class ProductService {
private final ProductRepository productRepository;
@Autowired
public ProductService(ProductRepository productRepository) {
this.productRepository = productRepository;
}
public List<Product> findAllProducts() {
return productRepository.findAll();
}
public Optional<Product> findProductById(String id) {
return productRepository.findById(id);
}
public List<Product> findProductsByName(String name) {
return productRepository.findByNameContainingIgnoreCase(name);
}
public List<Product> findProductsByMaxPrice(BigDecimal maxPrice) {
return productRepository.findByPriceLessThanEqual(maxPrice);
}
public Product saveProduct(Product product) {
return productRepository.save(product);
}
public void deleteProduct(String id) {
productRepository.deleteById(id);
}
}
Create src/main/java/com/example/demo/controller/ProductController.java
:
package com.example.demo.controller;
import com.example.demo.model.Product;
import com.example.demo.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.List;
@RestController
@RequestMapping("/api/products")
public class ProductController {
private final ProductService productService;
@Autowired
public ProductController(ProductService productService) {
this.productService = productService;
}
@GetMapping
public List<Product> getAllProducts() {
return productService.findAllProducts();
}
@GetMapping("/{id}")
public ResponseEntity<Product> getProductById(@PathVariable String id) {
return productService.findProductById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@GetMapping("/search")
public List<Product> searchProducts(
@RequestParam(required = false) String name,
@RequestParam(required = false) BigDecimal maxPrice) {
if (name != null && maxPrice != null) {
// If both parameters are provided, you might want to implement a custom method
return productService.findProductsByName(name);
} else if (name != null) {
return productService.findProductsByName(name);
} else if (maxPrice != null) {
return productService.findProductsByMaxPrice(maxPrice);
} else {
return productService.findAllProducts();
}
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Product createProduct(@RequestBody Product product) {
return productService.saveProduct(product);
}
@PutMapping("/{id}")
public ResponseEntity<Product> updateProduct(@PathVariable String id, @RequestBody Product product) {
return productService.findProductById(id)
.map(existingProduct -> {
product.setId(id);
return ResponseEntity.ok(productService.saveProduct(product));
})
.orElse(ResponseEntity.notFound().build());
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteProduct(@PathVariable String id) {
return productService.findProductById(id)
.map(product -> {
productService.deleteProduct(id);
return ResponseEntity.noContent().<Void>build();
})
.orElse(ResponseEntity.notFound().build());
}
}
- Install MongoDB if not already installed (download from the MongoDB website or use a package manager)
- Start MongoDB (typically runs on port 27017)
- MongoDB will automatically create the database
mydatabase
when you first save data to it
Create all the directories and files as outlined in the project structure section. You can do this manually or use a Spring Initializer.
Copy the provided build.gradle
and settings.gradle
files to your project root.
Copy the provided application.yaml
file to src/main/resources/
.
Copy the provided Java class files to their respective directories:
-
Application.java
tosrc/main/java/com/example/demo/
-
Product.java
tosrc/main/java/com/example/demo/model/
-
ProductRepository.java
tosrc/main/java/com/example/demo/repository/
-
ProductService.java
tosrc/main/java/com/example/demo/service/
-
ProductController.java
tosrc/main/java/com/example/demo/controller/
You can run the application using Gradle:
./gradlew bootRun
Or from your IDE by running the Application.java
class.
Once the application is running, you can test the API endpoints:
curl -X POST http://localhost:8080/api/products \
-H "Content-Type: application/json" \
-d '{"name":"Test Product","description":"A test product","price":19.99}'
curl http://localhost:8080/api/products
curl http://localhost:8080/api/products/60f7a5d3e1b7346bc1f13b45
curl "http://localhost:8080/api/products/search?name=test"
curl "http://localhost:8080/api/products/search?maxPrice=50"
To connect to MongoDB Atlas instead of a local MongoDB instance:
- Update the
application.yaml
to use a connection URI:
spring:
data:
mongodb:
uri: mongodb+srv://username:password@cluster0.mongodb.net/mydatabase
To improve query performance, you can add indexes to your document fields:
@Document(collection = "products")
@CompoundIndex(name = "name_price_idx", def = "{'name': 1, 'price': 1}")
public class Product {
@Id
private String id;
@Indexed
private String name;
private String description;
private BigDecimal price;
}
To store large files (e.g., images, documents):
// Add to build.gradle dependencies
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb-reactive'
Then create a configuration class:
@Configuration
public class GridFsConfig {
@Bean
public GridFsTemplate gridFsTemplate(MongoDatabaseFactory mongoDbFactory,
MongoTemplate mongoTemplate) {
return new GridFsTemplate(mongoDbFactory, mongoTemplate.getConverter());
}
}
MongoDB is a document-oriented database, which contrasts with relational databases:
- Documents contain all related data in a single JSON-like structure
- Documents in a collection can have different fields (schema flexibility)
- No need for complex joins across multiple tables
MongoDB data modelling generally follows two patterns:
- Embedded documents - When data is accessed together (1:1 or 1:few relationships):
@Document
public class Order {
@Id
private String id;
private String customerName;
// Embed the items directly
private List<OrderItem> items;
}
- References - When data is accessed independently (1:many or many:many):
@Document
public class Order {
@Id
private String id;
private String customerName;
// Store only IDs, not embedded documents
private List<String> itemIds;
}
Spring Data MongoDB supports various query creation methods:
// Find documents where a field equals a value
List<Product> findByCategory(String category);
// Using logical operators (AND, OR)
List<Product> findByCategoryAndPriceLessThan(String category, BigDecimal price);
// Using sorting
List<Product> findByNameContainingOrderByPriceAsc(String name);
// Using projections (return only specific fields)
@Query(value = "{ 'name': ?0 }", fields = "{ 'name': 1, 'price': 1 }")
List<Product> findProductNameAndPriceByName(String name);
For complex analytics, you can use MongoDB's aggregation framework:
@Autowired
private MongoTemplate mongoTemplate;
public List<DBObject> getProductStatsByCategory() {
TypedAggregation<Product> aggregation = Aggregation.newAggregation(
Product.class,
Aggregation.group("category")
.count().as("count")
.avg("price").as("avgPrice")
.max("price").as("maxPrice")
.min("price").as("minPrice"),
Aggregation.sort(Sort.Direction.DESC, "count")
);
AggregationResults<DBObject> results = mongoTemplate.aggregate(
aggregation, DBObject.class);
return results.getMappedResults();
}
You now have a complete Spring Boot 3 application with MongoDB connectivity using Java 17, Gradle 8, and Lombok. This empty project provides the foundation for building any document-oriented database application. Feel free to extend it with additional document models, queries, and business logic as needed.
Remember that MongoDB's flexibility allows for rapid development and iteration compared to traditional relational databases. You can easily evolve your document schemas over time without complex migration scripts.