Spring Boot with MongoDB

1 minute read

MongoDB is a document database — no tables, no schemas, just JSON-like documents. Spring Data MongoDB makes it feel just like JPA.


Setup

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
docker run -d --name bookstore-mongo -p 27017:27017 mongo:7
spring:
  data:
    mongodb:
      uri: mongodb://localhost:27017/bookstore

Document Entity

@Document(collection = "books")
public class Book {

    @Id
    private String id;          // MongoDB uses String IDs by default

    @Field("title")
    @Indexed
    private String title;

    private String author;

    @Indexed(unique = true)
    private String isbn;

    private BigDecimal price;

    private List<String> tags;                  // arrays
    private Map<String, Object> metadata;       // nested documents

    @CreatedDate
    private LocalDateTime createdAt;

    @LastModifiedDate
    private LocalDateTime updatedAt;
}

Embedded Documents

@Document(collection = "books")
public class Book {
    @Id
    private String id;
    private String title;

    private Publisher publisher;        // embedded (no separate collection)
    private List<Review> reviews;       // embedded array
}

// No @Document — this is embedded, not a separate collection
public class Publisher {
    private String name;
    private String country;
}

public class Review {
    private String reviewer;
    private int rating;
    private String comment;
    private LocalDate date;
}

MongoDB document:

{
  "_id": "665a...",
  "title": "Clean Code",
  "publisher": { "name": "Prentice Hall", "country": "US" },
  "reviews": [
    { "reviewer": "Alice", "rating": 5, "comment": "Great book!" }
  ]
}

MongoRepository

public interface BookRepository extends MongoRepository<Book, String> {

    List<Book> findByAuthor(String author);
    Optional<Book> findByIsbn(String isbn);
    List<Book> findByTagsContaining(String tag);
    List<Book> findByPriceGreaterThan(BigDecimal price);
    Page<Book> findByAuthor(String author, Pageable pageable);

    @Query("{ 'title': { $regex: ?0, $options: 'i' } }")
    List<Book> searchByTitle(String keyword);

    @Query("{ 'reviews.rating': { $gte: ?0 } }")
    List<Book> findByMinReviewRating(int minRating);
}

MongoTemplate — Complex Queries

@Service
public class BookService {

    private final MongoTemplate mongoTemplate;

    public List<Book> searchBooks(String keyword, BigDecimal minPrice) {
        Criteria criteria = new Criteria();

        if (keyword != null) {
            criteria.and("title").regex(keyword, "i");
        }
        if (minPrice != null) {
            criteria.and("price").gte(minPrice);
        }

        Query query = new Query(criteria).with(Sort.by(Sort.Direction.DESC, "price"));
        return mongoTemplate.find(query, Book.class);
    }
}

Aggregation

public List<AuthorStats> getAuthorStats() {
    Aggregation agg = Aggregation.newAggregation(
        Aggregation.group("author")
            .count().as("bookCount")
            .avg("price").as("avgPrice"),
        Aggregation.sort(Sort.Direction.DESC, "bookCount")
    );

    return mongoTemplate.aggregate(agg, "books", AuthorStats.class)
        .getMappedResults();
}

Key Takeaways

  1. @Document maps to MongoDB collections (like @Entity for JPA).
  2. MongoDB uses String IDs and embedded documents instead of joins.
  3. MongoRepository works just like JpaRepository — derived queries, pagination.
  4. Use MongoTemplate for complex queries and aggregations.
  5. Embed related data when it’s always accessed together; reference when it’s independent.

What’s Next?

Next: Spring Boot with Redis — caching, session storage, and @Cacheable.

Updated: