Stage 13: connect the Stage 12 application to MongoDB Atlas
Learning path / Stage 13 of 37
mvn spring-boot:run. Use the port specified for this stage and stop with Ctrl+C.Goal: keep our Stage 12 settings and bean configuration, but load the product table and details from MongoDB. Stage 11 introduced database CRUD; this stage focuses on configuring and verifying the connection in the Stage 12 application.
Download spring-products-mongodb-connectivity.zip · Complete file-by-file guide
1. Start from Stage 12
Extract this project into a new folder. Use JDK 21+ and Maven 3.9+. Open a terminal in the folder containing pom.xml. The first build needs internet access to download dependencies. This project defaults to port 8091. It can run alongside Stage 12 on 8090. It includes every source file; do not copy fragments over the older project.
java -version
javac -version
mvn -version2. Understand the one storage change
Stage 12 reads the product text file into memory. In this stage, ProductRepository extends MongoRepository<Product, Integer> and FileProductRepository is removed. ProductService keeps its existing stock policy and still receives CatalogSettings. CatalogConfig still supplies the JsonMapper and settings beans.
The text file is now only seed input: ProductDataSeeder copies its 30 products into an empty products collection at startup. If that collection already contains products, it is left unchanged. Subsequent requests read MongoDB. Deleting every product and restarting will seed again. Use the separate learning database spring_stage13_products.
3. Add the MongoDB dependency
The supplied pom.xml already contains this dependency. It brings the Spring Data MongoDB and driver libraries into the application.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>4. Prepare your Atlas connection
- Open your Atlas project and create or choose an available cluster. Wait until it is ready.
- Create a database user with read/write access to spring_stage13_products. This database user is separate from the account you use to sign into the Atlas website.
- In Network Access, add your current public IP address to the IP access list. If your network changes, update that entry.
- Choose the cluster’s Connect option, then Drivers and Java. Copy its connection string. Use the hostname Atlas gives you.
- Replace the username and password placeholders. Percent-encode reserved characters in the credential values; for example @ becomes %40 in a password. Do not encode the entire URI or double-encode an already encoded password.
Example shape only: mongodb+srv://YOUR_USER:YOUR_ENCODED_PASSWORD@YOUR_CLUSTER.mongodb.net/?retryWrites=true&w=majority. YOUR_USER, YOUR_ENCODED_PASSWORD and YOUR_CLUSTER are placeholders, not runnable credentials. See the complete Atlas setup guide for the installation and setup details.
5. Read the connection settings
# HTTP port: change this if another application uses 8091.
server.port=8091
# Set MONGODB_URI in your terminal to use your own Atlas cluster.
# Without that variable, use a running local MongoDB server on 27017.
spring.mongodb.uri=${MONGODB_URI:mongodb://localhost:27017}
spring.mongodb.database=${MONGODB_DATABASE:spring_stage13_products}
server.port is our web-server port. spring.mongodb.uri is the database connection address. spring.mongodb.database selects the database used by this application. The expression with MONGODB_URI reads an environment variable; the part after the colon is the fallback when the variable is absent. This tutorial uses Boot 4.1.1 property names.
6. Set the variables and run
Choose the commands for your terminal. Replace the URI placeholders with your own values. Set the variables in the same terminal where you start Maven. These commands affect this terminal session; repeat them in a new terminal. A .env file is not automatically loaded by these commands. Avoid committing or sharing your real URI.
On Windows, use PowerShell for the URI commands. If your terminal is Command Prompt, first enter the following command to switch; this also avoids Command Prompt interpreting percent signs in encoded passwords.
powershell$env:MONGODB_URI='mongodb+srv://YOUR_USER:YOUR_ENCODED_PASSWORD@YOUR_CLUSTER.mongodb.net/?retryWrites=true&w=majority'
$env:MONGODB_DATABASE='spring_stage13_products'
mvn spring-boot:runexport MONGODB_URI='mongodb+srv://YOUR_USER:YOUR_ENCODED_PASSWORD@YOUR_CLUSTER.mongodb.net/?retryWrites=true&w=majority'
export MONGODB_DATABASE='spring_stage13_products'
mvn spring-boot:runLocal alternative: with a running local MongoDB server, use mongodb://127.0.0.1:27017 as MONGODB_URI instead. Keep the same database name. Running the app does not install or start MongoDB. The MongoDB port and the application’s HTTP port are separate.
7. Verify the connection in the browser
Keep the terminal open and visit http://localhost:8091/database-info. With a fresh database, expect:
Connected to MongoDB
Database: spring_stage13_products
Ping result: 1.0
Product count: 30The endpoint executes a ping against the configured database server, then counts products through the repository. It displays no URI, username or password. MongoTemplate is a Spring Data helper supplied by Boot; we inject it through the constructor just like our other dependencies. It is used for this diagnostic page, while product list and details reads stay in ProductRepository. This page is a local learning diagnostic; a public application should protect or remove it.
8. Verify the product pages
- Open http://localhost:8091/products: expect 30 product rows on the first run, currency INR and minimum stock 20.
- Click More details for product 1: the second Thymeleaf template shows its fields.
- Click Products meeting the stock policy: expect 24 rows for the supplied seed data. The service still applies minimumStock from CatalogSettings.
- Open /bean-info: productRepository replaces fileProductRepository in the bean listing. The registered beans and singleton checks should be true.
- In Atlas Browse Collections, choose spring_stage13_products → products. Find numeric _id 1, change its name to Stage 13 connection verified, and save. Refresh /products/1: the new name appears.
- Stop the app with Ctrl+C and restart it with the same database settings. The changed name remains. The existing collection is not overwritten by the file. Restore the name in Atlas when finished.
If you use a local server, perform the same document edit in MongoDB Compass connected to that server. An unavailable product ID such as /products/99999 returns 404. This stage intentionally keeps a read-only product UI; Stage 11 already contains create, update and delete forms.
9. Who creates which objects?
| Object | How it becomes available |
|---|---|
| JsonMapper and CatalogSettings | Our @Bean factory methods in CatalogConfig. |
| Controller, service, file reader and seeder | Our component annotations and constructor injection. |
| MongoDB client and MongoTemplate | Boot’s automatic configuration uses the MongoDB dependency and connection settings. |
| ProductRepository implementation | Spring Data creates the supported repository implementation. |
We do not need another manual @Bean method for the connection in this example. Boot already supplies that setup. Having a client bean does not prove the remote server is reachable; the successful ping and repository read verify actual communication.
10. Troubleshooting
| Symptom | What to check |
|---|---|
| Authentication failed | Database username/password, encoded credential characters and permissions for spring_stage13_products. |
| Connection timeout | Cluster readiness, current IP access-list entry, network/firewall access and the copied hostname. For a local URI, confirm the local server is running. |
| SRV / DNS lookup failure | Use the exact Atlas hostname. Check whether your network’s DNS can resolve it. Do not replace it with an invented hostname. |
| App still connects to localhost | MONGODB_URI was not set in the terminal or IDE that launches Java. Set it there and restart. |
| Wrong count or old products | /database-info shows the selected database. An existing collection is intentionally not reseeded; check the database name and existing documents. |
| Port 8091 already in use | Change server.port to a free port, restart and use the changed URL. |
| Startup fails before the page opens | The seeder needs a working database connection. Read the first connection error in the terminal and fix the URI, access or server status, then restart. |
Changed files to study
The ZIP and standalone README.html contain every file. These complete files show the connection changes.
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.1.1</version>
<relativePath/>
</parent>
<groupId>com.edupoly</groupId>
<artifactId>spring-products-mongodb-connectivity</artifactId>
<version>1.0.0</version>
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
src/main/resources/application.properties
# HTTP port: change this if another application uses 8091.
server.port=8091
# Set MONGODB_URI in your terminal to use your own Atlas cluster.
# Without that variable, use a running local MongoDB server on 27017.
spring.mongodb.uri=${MONGODB_URI:mongodb://localhost:27017}
spring.mongodb.database=${MONGODB_DATABASE:spring_stage13_products}
src/main/java/com/edupoly/products/Product.java
package com.edupoly.products;
import java.math.BigDecimal;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Document("products")
public class Product {
@Id
private int id;
private String name;
private String category;
private BigDecimal price;
private String brand;
private int stock;
private String description;
private int warrantyMonths;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public int getStock() {
return stock;
}
public void setStock(int stock) {
this.stock = stock;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public int getWarrantyMonths() {
return warrantyMonths;
}
public void setWarrantyMonths(int warrantyMonths) {
this.warrantyMonths = warrantyMonths;
}
}
src/main/java/com/edupoly/products/ProductRepository.java
package com.edupoly.products;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface ProductRepository extends MongoRepository<Product, Integer> {
}
src/main/java/com/edupoly/products/ProductDataSeeder.java
package com.edupoly.products;
import java.util.Arrays;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class ProductDataSeeder implements CommandLineRunner {
private final ProductRepository repository;
private final ProductFileReader reader;
public ProductDataSeeder(ProductRepository repository, ProductFileReader reader) {
this.repository = repository;
this.reader = reader;
}
@Override
public void run(String... args) {
if (repository.count() == 0) {
repository.saveAll(Arrays.asList(reader.getProducts()));
System.out.println("Seeded 30 products into the empty demo database");
}
}
}
src/main/java/com/edupoly/products/DatabaseInfoController.java
package com.edupoly.products;
import org.bson.Document;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class DatabaseInfoController {
private final MongoTemplate mongoTemplate;
private final ProductRepository repository;
public DatabaseInfoController(MongoTemplate mongoTemplate,
ProductRepository repository) {
this.mongoTemplate = mongoTemplate;
this.repository = repository;
}
@GetMapping(value = "/database-info", produces = "text/plain")
@ResponseBody
public String databaseInfo() {
var database = mongoTemplate.getDb();
Document result = database.runCommand(new Document("ping", 1));
return "Connected to MongoDB\n"
+ "Database: " + database.getName() + "\n"
+ "Ping result: " + result.get("ok") + "\n"
+ "Product count: " + repository.count();
}
}
src/main/java/com/edupoly/products/BeanInfoController.java
package com.edupoly.products;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class BeanInfoController {
private final ConfigurableApplicationContext context;
public BeanInfoController(ConfigurableApplicationContext context) {
this.context = context;
}
@GetMapping(value = "/bean-info", produces = "text/plain")
@ResponseBody
public String beanInfo() {
StringBuilder text = new StringBuilder();
for (String name : new String[] {"jsonMapper", "catalogSettings", "productFileReader",
"productRepository", "productService", "productController"}) {
text.append(name)
.append(" | definition registered: ").append(context.containsBeanDefinition(name))
.append(" | singleton: ").append(context.isSingleton(name)).append("\n");
}
text.append("Same settings bean on two lookups: ")
.append(context.getBean(CatalogSettings.class) == context.getBean(CatalogSettings.class));
return text.toString();
}
}
Practice
Explain which settings choose the web port, database server and database name. Point to one bean supplied by your @Bean method and one supplied by Boot. Verify that a database edit remains after restarting the Java app.
References: Spring Boot MongoDB support, Atlas driver connections.
After Stage 13: the Spring words to recognize
| Term | Our concrete example | Its place in Spring |
|---|---|---|
| Connection URI | MONGODB_URI identifies your MongoDB server/cluster and connection credentials. | Spring Boot reads spring.mongodb.uri to configure database connectivity. |
| Auto-configuration — observed | We add the MongoDB starter and connection settings. | Boot supplies MongoDB infrastructure beans without us writing a client factory. |
| MongoTemplate | The helper injected into DatabaseInfoController. | A Spring Data helper used here only to run a connection check. Product reads still use our repository. |
| Repository — reused | ProductRepository extends MongoRepository. | Spring Data supplies the implementation that reads product documents. |
Say it simply: I provide the database address and credentials; Boot configures the connection infrastructure; Spring Data supplies the repository; my service uses that repository.
Concepts used in this stage
Use the right column for a quick reminder, or open a concept above for its full explanation.