Links#
https://www.mongodb.com/docs/languages/java/
https://www.mongodb.com/docs/drivers/java/sync/current/getting-started/quick-reference/
https://www.mongodb.com/docs/drivers/java/sync/current/data-formats/document-data-format-pojo/
https://www.mongodb.com/docs/manual/reference/connection-string/
https://www.mongodb.com/docs/manual/crud/
1. What You Need To Know#
MongoDB concepts:
database:
类似 MySQL database
collection:
类似 table,但不要求每条 document schema 完全一致
document:
BSON document,类似 JSON object
一条 document 最大 16 MB
_id:
每条 document 的 primary key
不指定时 MongoDB 会自动生成 ObjectId
index:
query performance 的核心
没有合适 index,高 QPS query 很容易变成 COLLSCAN
replica set:
production baseline
primary handles writes
secondary replicates from primary
connection string:
driver 通过 URI 连接 MongoDB
production replica set 应该写多个 host + replicaSet
mental model:
MongoClient
-> MongoDatabase
-> MongoCollection<Document>
-> insertOne / find / updateOne / deleteOne
2. Hello World Goal#
we will build:
Java CLI app
connect to MongoDB
create collection index
insert one order
query order by user_id
update order status
delete order
language:
Java
driver:
MongoDB Java Sync Driver
3. Maven Dependency#
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>me.xiezhao.demo</groupId>
<artifactId>mongodb-hello-world</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId>
<version>5.5.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.5.0</version>
</plugin>
</plugins>
</build>
</project>
4. Configuration#
local dev#
# application.properties
mongodb.uri=mongodb://root:change-me@localhost:27017/order?authSource=admin&appName=order-api
mongodb.database=order
mongodb.collection=orders
production replica set#
mongodb.uri=mongodb://order_app:<password>@mongo-1:27017,mongo-2:27017,mongo-3:27017/order?replicaSet=rs0&authSource=admin&appName=order-api&retryWrites=true&w=majority
mongodb.database=order
mongodb.collection=orders
important options:
replicaSet=rs0
tell driver this is a replica set
authSource=admin
user is created in admin database
appName=order-api
appears in MongoDB logs / profiler
retryWrites=true
driver can retry supported write operations
w=majority
write acknowledged by majority
5. Start MongoDB Locally#
docker run -d \
--name mongo \
-p 27017:27017 \
-e MONGO_INITDB_ROOT_USERNAME=root \
-e MONGO_INITDB_ROOT_PASSWORD=change-me \
mongo:8
mongosh "mongodb://root:change-me@localhost:27017/admin"
6. Project Structure#
mongodb-hello-world
├── pom.xml
└── src
└── main
├── java
│ └── me
│ └── xiezhao
│ └── demo
│ └── MongoHelloWorld.java
└── resources
└── application.properties
7. Java Hello World#
package me.xiezhao.demo;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Indexes;
import com.mongodb.client.model.Updates;
import org.bson.Document;
import org.bson.conversions.Bson;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.time.Instant;
import java.util.Properties;
public class MongoHelloWorld {
public static void main(String[] args) throws IOException {
Properties properties = loadProperties();
String uri = properties.getProperty("mongodb.uri");
String databaseName = properties.getProperty("mongodb.database");
String collectionName = properties.getProperty("mongodb.collection");
try (MongoClient client = MongoClients.create(uri)) {
MongoDatabase database = client.getDatabase(databaseName);
MongoCollection<Document> orders = database.getCollection(collectionName);
orders.createIndex(Indexes.compoundIndex(
Indexes.ascending("user_id"),
Indexes.descending("created_at")
));
String orderId = "o-1001";
String userId = "u-1001";
Document order = new Document("order_id", orderId)
.append("user_id", userId)
.append("status", "PENDING")
.append("amount", new BigDecimal("99.90"))
.append("created_at", Instant.now());
orders.insertOne(order);
Bson queryByUser = Filters.eq("user_id", userId);
Document found = orders.find(queryByUser)
.sort(Indexes.descending("created_at"))
.first();
System.out.println("found order: " + found.toJson());
Bson queryByOrderId = Filters.eq("order_id", orderId);
orders.updateOne(
queryByOrderId,
Updates.combine(
Updates.set("status", "PAID"),
Updates.set("paid_at", Instant.now())
)
);
Document paidOrder = orders.find(queryByOrderId).first();
System.out.println("paid order: " + paidOrder.toJson());
orders.deleteOne(queryByOrderId);
}
}
private static Properties loadProperties() throws IOException {
Properties properties = new Properties();
try (InputStream input = MongoHelloWorld.class
.getClassLoader()
.getResourceAsStream("application.properties")) {
if (input == null) {
throw new IllegalStateException("application.properties not found");
}
properties.load(input);
}
return properties;
}
}
8. Run#
mvn -q compile exec:java \
-Dexec.mainClass=me.xiezhao.demo.MongoHelloWorld
expected:
found order: {"_id": ..., "order_id": "o-1001", ...}
paid order: {"_id": ..., "order_id": "o-1001", "status": "PAID", ...}
9. How To Use In Real Project#
client lifecycle#
rule:
create MongoClient once when application starts
reuse it for all requests
close it when application stops
do not:
create MongoClient per request
public final class MongoProvider implements AutoCloseable {
private final MongoClient client;
private final MongoDatabase database;
public MongoProvider(String uri, String databaseName) {
this.client = MongoClients.create(uri);
this.database = client.getDatabase(databaseName);
}
public MongoCollection<Document> collection(String name) {
return database.getCollection(name);
}
@Override
public void close() {
client.close();
}
}
repository pattern#
public final class OrderRepository {
private final MongoCollection<Document> orders;
public OrderRepository(MongoCollection<Document> orders) {
this.orders = orders;
}
public void createOrder(String orderId, String userId, BigDecimal amount) {
Document order = new Document("order_id", orderId)
.append("user_id", userId)
.append("status", "PENDING")
.append("amount", amount)
.append("created_at", Instant.now());
orders.insertOne(order);
}
public Document findByOrderId(String orderId) {
return orders.find(Filters.eq("order_id", orderId)).first();
}
public void markPaid(String orderId) {
orders.updateOne(
Filters.and(
Filters.eq("order_id", orderId),
Filters.eq("status", "PENDING")
),
Updates.combine(
Updates.set("status", "PAID"),
Updates.set("paid_at", Instant.now())
)
);
}
}
repository should own:
collection name
query condition
update expression
index requirement
service should own:
business workflow
validation
transaction decision
10. Production Checklist#
before coding:
write access patterns first
design document shape
design indexes
decide consistency requirement
application config:
URI comes from env / secret manager
appName is set
replicaSet is set for replica set
retryWrites is enabled when suitable
write concern is explicit for critical data
code:
reuse MongoClient
do not create client per request
set query limit
use projection for large documents
avoid skip for deep pagination
use update operators, not full document replacement by default
operation:
create index before production traffic
review explain for hot queries
monitor slow query
monitor connection pool
monitor replication lag
11. Common Mistakes#
mistakes:
no index on hot query
full collection scan in API path
create MongoClient per request
large skip pagination
huge document / unbounded array
read from secondary without considering lag
use root user in application
expose MongoDB to public internet
no backup restore test