QuickStart


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