MongoDB 详细用法与 Java 集成完整指南
目录
- MongoDB 基础概念
- MongoDB 安装与配置
- MongoDB Shell 基本操作
- Java 环境准备
- Java MongoDB 驱动集成
- 连接配置
- 基本 CRUD 操作
- 高级查询操作
- 索引操作
- 聚合管道
- 事务处理
- Spring Boot 集成
- 最佳实践
1. MongoDB 基础概念
1.1 核心概念对比
SQL术语 | MongoDB术语 | 说明 |
---|---|---|
database | database | 数据库 |
table | collection | 集合(表) |
row | document | 文档(行) |
column | field | 字段(列) |
index | index | 索引 |
primary key | _id | 主键 |
1.2 文档结构
{
"_id": ObjectId("507f1f77bcf86cd799439011"),
"name": "John Doe",
"age": 30,
"email": "john@example.com",
"address": {
"street": "123 Main St",
"city": "New York",
"zipcode": "10001"
},
"tags": ["developer", "mongodb", "java"],
"createdAt": ISODate("2023-01-01T00:00:00Z")
}
2. MongoDB 安装与配置
2.1 Windows 安装
# 1. 下载 MongoDB Community Server
# https://www.mongodb.com/try/download/community
# 2. 安装后启动服务
net start MongoDB
# 3. 连接到 MongoDB
mongo
2.2 Linux 安装 (Ubuntu)
# 1. 导入公钥
wget -qO - https://www.mongodb.org/static/pgp/server-6.0.asc | sudo apt-key add -
# 2. 添加仓库
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/6.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-6.0.list
# 3. 更新包列表并安装
sudo apt-get update
sudo apt-get install -y mongodb-org
# 4. 启动服务
sudo systemctl start mongod
sudo systemctl enable mongod
# 5. 验证安装
mongosh
2.3 Docker 安装
# 拉取 MongoDB 镜像
docker pull mongo:latest
# 运行 MongoDB 容器
docker run -d \
--name mongodb \
-p 27017:27017 \
-e MONGO_INITDB_ROOT_USERNAME=admin \
-e MONGO_INITDB_ROOT_PASSWORD=password \
-v mongodb_data:/data/db \
mongo:latest
# 连接到容器
docker exec -it mongodb mongosh -u admin -p password
3. MongoDB Shell 基本操作
3.1 数据库操作
// 显示所有数据库
show dbs
// 使用/创建数据库
use myapp
// 显示当前数据库
db
// 删除数据库
db.dropDatabase()
3.2 集合操作
// 显示所有集合
show collections
// 创建集合
db.createCollection("users")
// 删除集合
db.users.drop()
3.3 文档操作
// 插入单个文档
db.users.insertOne({
name: "Alice",
age: 25,
email: "alice@example.com"
})
// 插入多个文档
db.users.insertMany([
{ name: "Bob", age: 30, email: "bob@example.com" },
{ name: "Charlie", age: 35, email: "charlie@example.com" }
])
// 查询所有文档
db.users.find()
// 条件查询
db.users.find({ age: { $gte: 30 } })
// 更新文档
db.users.updateOne(
{ name: "Alice" },
{ $set: { age: 26 } }
)
// 删除文档
db.users.deleteOne({ name: "Bob" })
4. Java 环境准备
4.1 系统要求
- Java 8 或更高版本
- Maven 3.3+ 或 Gradle 4.0+
- MongoDB 4.0 或更高版本
4.2 Maven 项目结构
my-mongodb-app/
├── pom.xml
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ ├── Main.java
│ │ │ ├── config/
│ │ │ ├── model/
│ │ │ ├── dao/
│ │ │ └── service/
│ │ └── resources/
│ │ └── application.properties
│ └── test/
└── target/
5. Java MongoDB 驱动集成
5.1 Maven 依赖配置
<?xml version="1.0" encoding="UTF-8"?>
<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
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>mongodb-java-example</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- MongoDB Java Driver -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId>
<version>4.11.1</version>
</dependency>
<!-- 可选: 异步驱动 -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-reactivestreams</artifactId>
<version>4.11.1</version>
</dependency>
<!-- JSON 处理 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.7</version>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
5.2 Gradle 依赖配置
plugins {
id 'java'
id 'application'
}
group = 'com.example'
version = '1.0.0'
sourceCompatibility = '11'
repositories {
mavenCentral()
}
dependencies {
// MongoDB Java Driver
implementation 'org.mongodb:mongodb-driver-sync:4.11.1'
implementation 'org.mongodb:mongodb-driver-reactivestreams:4.11.1'
// JSON 处理
implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2'
// 日志
implementation 'org.slf4j:slf4j-simple:2.0.7'
// 测试
testImplementation 'junit:junit:4.13.2'
}
application {
mainClass = 'com.example.Main'
}
6. 连接配置
6.1 基本连接配置
package com.example.config;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.ServerAddress;
import com.mongodb.MongoCredential;
import java.util.Arrays;
public class MongoDBConfig {
private static final String DATABASE_NAME = "myapp";
private static final String CONNECTION_STRING = "mongodb://localhost:27017";
private static MongoClient mongoClient;
private static MongoDatabase database;
// 简单连接方式
public static MongoDatabase getDatabase() {
if (database == null) {
mongoClient = MongoClients.create(CONNECTION_STRING);
database = mongoClient.getDatabase(DATABASE_NAME);
}
return database;
}
// 详细连接配置
public static MongoDatabase getDatabaseWithOptions() {
if (database == null) {
// 创建连接字符串
ConnectionString connectionString = new ConnectionString(
"mongodb://username:password@localhost:27017/myapp?authSource=admin"
);
// 或者使用 MongoClientSettings 进行详细配置
MongoClientSettings settings = MongoClientSettings.builder()
.applyConnectionString(connectionString)
.retryWrites(true)
.build();
mongoClient = MongoClients.create(settings);
database = mongoClient.getDatabase(DATABASE_NAME);
}
return database;
}
// 集群连接配置
public static MongoDatabase getClusterDatabase() {
if (database == null) {
// 服务器地址列表
ServerAddress seed1 = new ServerAddress("server1.example.com", 27017);
ServerAddress seed2 = new ServerAddress("server2.example.com", 27017);
ServerAddress seed3 = new ServerAddress("server3.example.com", 27017);
// 认证信息
MongoCredential credential = MongoCredential.createCredential(
"username", "admin", "password".toCharArray()
);
// 客户端设置
MongoClientSettings settings = MongoClientSettings.builder()
.applyToClusterSettings(builder ->
builder.hosts(Arrays.asList(seed1, seed2, seed3)))
.credential(credential)
.build();
mongoClient = MongoClients.create(settings);
database = mongoClient.getDatabase(DATABASE_NAME);
}
return database;
}
// 关闭连接
public static void close() {
if (mongoClient != null) {
mongoClient.close();
}
}
}
6.2 连接池配置
package com.example.config;
import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.connection.ConnectionPoolSettings;
import java.util.concurrent.TimeUnit;
public class ConnectionPoolConfig {
public static MongoClient createMongoClientWithPool() {
ConnectionString connectionString = new ConnectionString(
"mongodb://localhost:27017/myapp"
);
// 连接池设置
ConnectionPoolSettings poolSettings = ConnectionPoolSettings.builder()
.maxSize(20) // 最大连接数
.minSize(5) // 最小连接数
.maxWaitTime(2, TimeUnit.MINUTES) // 最大等待时间
.maxConnectionLifeTime(30, TimeUnit.MINUTES) // 连接最大生存时间
.maxConnectionIdleTime(10, TimeUnit.MINUTES) // 连接最大空闲时间
.build();
MongoClientSettings settings = MongoClientSettings.builder()
.applyConnectionString(connectionString)
.applyToConnectionPoolSettings(builder ->
builder.applySettings(poolSettings))
.build();
return MongoClients.create(settings);
}
}
7. 基本 CRUD 操作
7.1 实体类定义
package com.example.model;
import org.bson.types.ObjectId;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.time.LocalDateTime;
import java.util.List;
public class User {
@JsonProperty("_id")
private ObjectId id;
private String name;
private String email;
private Integer age;
private Address address;
private List<String> tags;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
// 构造函数
public User() {
this.createdAt = LocalDateTime.now();
}
public User(String name, String email, Integer age) {
this();
this.name = name;
this.email = email;
this.age = age;
}
// Getter 和 Setter 方法
public ObjectId getId() { return id; }
public void setId(ObjectId id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
public Address getAddress() { return address; }
public void setAddress(Address address) { this.address = address; }
public List<String> getTags() { return tags; }
public void setTags(List<String> tags) { this.tags = tags; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
public LocalDateTime getUpdatedAt() { return updatedAt; }
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", age=" + age +
", address=" + address +
", tags=" + tags +
", createdAt=" + createdAt +
", updatedAt=" + updatedAt +
'}';
}
}
// 地址类
class Address {
private String street;
private String city;
private String zipcode;
private String country;
// 构造函数
public Address() {}
public Address(String street, String city, String zipcode, String country) {
this.street = street;
this.city = city;
this.zipcode = zipcode;
this.country = country;
}
// Getter 和 Setter 方法
public String getStreet() { return street; }
public void setStreet(String street) { this.street = street; }
public String getCity() { return city; }
public void setCity(String city) { this.city = city; }
public String getZipcode() { return zipcode; }
public void setZipcode(String zipcode) { this.zipcode = zipcode; }
public String getCountry() { return country; }
public void setCountry(String country) { this.country = country; }
@Override
public String toString() {
return "Address{" +
"street='" + street + '\'' +
", city='" + city + '\'' +
", zipcode='" + zipcode + '\'' +
", country='" + country + '\'' +
'}';
}
}
7.2 DAO 层实现
package com.example.dao;
import com.example.config.MongoDBConfig;
import com.example.model.User;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Updates;
import com.mongodb.client.model.Sorts;
import com.mongodb.client.model.Projections;
import com.mongodb.client.result.InsertOneResult;
import com.mongodb.client.result.UpdateResult;
import com.mongodb.client.result.DeleteResult;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.bson.types.ObjectId;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
public class UserDAO {
private final MongoCollection<Document> collection;
public UserDAO() {
MongoDatabase database = MongoDBConfig.getDatabase();
this.collection = database.getCollection("users");
}
// 创建用户 - 插入单个文档
public ObjectId createUser(User user) {
try {
Document doc = userToDocument(user);
InsertOneResult result = collection.insertOne(doc);
return result.getInsertedId().asObjectId().getValue();
} catch (Exception e) {
throw new RuntimeException("Error creating user: " + e.getMessage(), e);
}
}
// 批量创建用户
public List<ObjectId> createUsers(List<User> users) {
try {
List<Document> documents = new ArrayList<>();
for (User user : users) {
documents.add(userToDocument(user));
}
collection.insertMany(documents);
List<ObjectId> insertedIds = new ArrayList<>();
for (Document doc : documents) {
insertedIds.add(doc.getObjectId("_id"));
}
return insertedIds;
} catch (Exception e) {
throw new RuntimeException("Error creating users: " + e.getMessage(), e);
}
}
// 根据 ID 查询用户
public User findById(ObjectId id) {
try {
Document doc = collection.find(Filters.eq("_id", id)).first();
return doc != null ? documentToUser(doc) : null;
} catch (Exception e) {
throw new RuntimeException("Error finding user by id: " + e.getMessage(), e);
}
}
// 根据邮箱查询用户
public User findByEmail(String email) {
try {
Document doc = collection.find(Filters.eq("email", email)).first();
return doc != null ? documentToUser(doc) : null;
} catch (Exception e) {
throw new RuntimeException("Error finding user by email: " + e.getMessage(), e);
}
}
// 查询所有用户
public List<User> findAll() {
try {
List<User> users = new ArrayList<>();
for (Document doc : collection.find()) {
users.add(documentToUser(doc));
}
return users;
} catch (Exception e) {
throw new RuntimeException("Error finding all users: " + e.getMessage(), e);
}
}
// 条件查询 - 年龄范围
public List<User> findByAgeRange(int minAge, int maxAge) {
try {
List<User> users = new ArrayList<>();
Bson filter = Filters.and(
Filters.gte("age", minAge),
Filters.lte("age", maxAge)
);
for (Document doc : collection.find(filter)) {
users.add(documentToUser(doc));
}
return users;
} catch (Exception e) {
throw new RuntimeException("Error finding users by age range: " + e.getMessage(), e);
}
}
// 模糊查询 - 姓名包含关键词
public List<User> findByNameContaining(String keyword) {
try {
List<User> users = new ArrayList<>();
Bson filter = Filters.regex("name", ".*" + keyword + ".*", "i");
for (Document doc : collection.find(filter)) {
users.add(documentToUser(doc));
}
return users;
} catch (Exception e) {
throw new RuntimeException("Error finding users by name: " + e.getMessage(), e);
}
}
// 分页查询
public List<User> findWithPagination(int page, int pageSize) {
try {
List<User> users = new ArrayList<>();
int skip = (page - 1) * pageSize;
for (Document doc : collection.find()
.sort(Sorts.descending("createdAt"))
.skip(skip)
.limit(pageSize)) {
users.add(documentToUser(doc));
}
return users;
} catch (Exception e) {
throw new RuntimeException("Error finding users with pagination: " + e.getMessage(), e);
}
}
// 只查询特定字段
public List<User> findNamesAndEmails() {
try {
List<User> users = new ArrayList<>();
Bson projection = Projections.fields(
Projections.include("name", "email"),
Projections.exclude("_id")
);
for (Document doc : collection.find().projection(projection)) {
User user = new User();
user.setName(doc.getString("name"));
user.setEmail(doc.getString("email"));
users.add(user);
}
return users;
} catch (Exception e) {
throw new RuntimeException("Error finding names and emails: " + e.getMessage(), e);
}
}
// 更新用户
public boolean updateUser(ObjectId id, User user) {
try {
user.setUpdatedAt(LocalDateTime.now());
Bson filter = Filters.eq("_id", id);
Bson update = Updates.combine(
Updates.set("name", user.getName()),
Updates.set("email", user.getEmail()),
Updates.set("age", user.getAge()),
Updates.set("updatedAt", user.getUpdatedAt())
);
UpdateResult result = collection.updateOne(filter, update);
return result.getModifiedCount() > 0;
} catch (Exception e) {
throw new RuntimeException("Error updating user: " + e.getMessage(), e);
}
}
// 部分更新 - 只更新年龄
public boolean updateAge(ObjectId id, int newAge) {
try {
Bson filter = Filters.eq("_id", id);
Bson update = Updates.combine(
Updates.set("age", newAge),
Updates.set("updatedAt", LocalDateTime.now())
);
UpdateResult result = collection.updateOne(filter, update);
return result.getModifiedCount() > 0;
} catch (Exception e) {
throw new RuntimeException("Error updating user age: " + e.getMessage(), e);
}
}
// 批量更新
public long updateUsersInCity(String city, String newCountry) {
try {
Bson filter = Filters.eq("address.city", city);
Bson update = Updates.combine(
Updates.set("address.country", newCountry),
Updates.set("updatedAt", LocalDateTime.now())
);
UpdateResult result = collection.updateMany(filter, update);
return result.getModifiedCount();
} catch (Exception e) {
throw new RuntimeException("Error updating users in city: " + e.getMessage(), e);
}
}
// 删除用户
public boolean deleteUser(ObjectId id) {
try {
DeleteResult result = collection.deleteOne(Filters.eq("_id", id));
return result.getDeletedCount() > 0;
} catch (Exception e) {
throw new RuntimeException("Error deleting user: " + e.getMessage(), e);
}
}
// 根据条件删除多个用户
public long deleteUsersByAge(int maxAge) {
try {
Bson filter = Filters.lte("age", maxAge);
DeleteResult result = collection.deleteMany(filter);
return result.getDeletedCount();
} catch (Exception e) {
throw new RuntimeException("Error deleting users by age: " + e.getMessage(), e);
}
}
// 计数操作
public long countUsers() {
try {
return collection.countDocuments();
} catch (Exception e) {
throw new RuntimeException("Error counting users: " + e.getMessage(), e);
}
}
public long countUsersByAge(int minAge) {
try {
return collection.countDocuments(Filters.gte("age", minAge));
} catch (Exception e) {
throw new RuntimeException("Error counting users by age: " + e.getMessage(), e);
}
}
// 辅助方法 - User 转 Document
private Document userToDocument(User user) {
Document doc = new Document();
if (user.getId() != null) {
doc.put("_id", user.getId());
}
doc.put("name", user.getName());
doc.put("email", user.getEmail());
doc.put("age", user.getAge());
if (user.getAddress() != null) {
Document addressDoc = new Document()
.append("street", user.getAddress().getStreet())
.append("city", user.getAddress().getCity())
.append("zipcode", user.getAddress().getZipcode())
.append("country", user.getAddress().getCountry());
doc.put("address", addressDoc);
}
if (user.getTags() != null) {
doc.put("tags", user.getTags());
}
doc.put("createdAt", user.getCreatedAt());
doc.put("updatedAt", user.getUpdatedAt());
return doc;
}
// 辅助方法 - Document 转 User
private User documentToUser(Document doc) {
User user = new User();
user.setId(doc.getObjectId("_id"));
user.setName(doc.getString("name"));
user.setEmail(doc.getString("email"));
user.setAge(doc.getInteger("age"));
Document addressDoc = doc.get("address", Document.class);
if (addressDoc != null) {
Address address = new Address(
addressDoc.getString("street"),
addressDoc.getString("city"),
addressDoc.getString("zipcode"),
addressDoc.getString("country")
);
user.setAddress(address);
}
@SuppressWarnings("unchecked")
List<String> tags = doc.get("tags", List.class);
user.setTags(tags);
// 注意:这里需要处理日期类型转换
user.setCreatedAt((LocalDateTime) doc.get("createdAt"));
user.setUpdatedAt((LocalDateTime) doc.get("updatedAt"));
return user;
}
}
7.3 Service 层实现
package com.example.service;
import com.example.dao.UserDAO;
import com.example.model.User;
import org.bson.types.ObjectId;
import java.util.List;
public class UserService {
private final UserDAO userDAO;
public UserService() {
this.userDAO = new UserDAO();
}
// 创建用户
public ObjectId createUser(String name, String email, Integer age) {
// 业务逻辑验证
if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("Name cannot be empty");
}
if (email == null || !isValidEmail(email)) {
throw new IllegalArgumentException("Invalid email format");
}
if (age != null && (age < 0 || age > 150)) {
throw new IllegalArgumentException("Age must be between 0 and 150");
}
// 检查邮箱是否已存在
User existingUser = userDAO.findByEmail(email);
if (existingUser != null) {
throw new IllegalArgumentException("Email already exists");
}
User user = new User(name, email, age);
return userDAO.createUser(user);
}
// 获取用户
public User getUserById(String id) {
try {
ObjectId objectId = new ObjectId(id);
return userDAO.findById(objectId);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Invalid user ID format");
}
}
public User getUserByEmail(String email) {
return userDAO.findByEmail(email);
}
public List<User> getAllUsers() {
return userDAO.findAll();
}
public List<User> getUsersByAgeRange(int minAge, int maxAge) {
if (minAge < 0 || maxAge < 0 || minAge > maxAge) {
throw new IllegalArgumentException("Invalid age range");
}
return userDAO.findByAgeRange(minAge, maxAge);
}
public List<User> searchUsersByName(String keyword) {
if (keyword == null || keyword.trim().isEmpty()) {
throw new IllegalArgumentException("Search keyword cannot be empty");
}
return userDAO.findByNameContaining(keyword.trim());
}
public List<User> getUsersWithPagination(int page, int pageSize) {
if (page < 1 || pageSize < 1) {
throw new IllegalArgumentException("Page and pageSize must be positive");
}
return userDAO.findWithPagination(page, pageSize);
}
// 更新用户
public boolean updateUser(String id, String name, String email, Integer age) {
try {
ObjectId objectId = new ObjectId(id);
// 验证输入
if (name != null && name.trim().isEmpty()) {
throw new IllegalArgumentException("Name cannot be empty");
}
if (email != null && !isValidEmail(email)) {
throw new IllegalArgumentException("Invalid email format");
}
if (age != null && (age < 0 || age > 150)) {
throw new IllegalArgumentException("Age must be between 0 and 150");
}
// 检查用户是否存在
User existingUser = userDAO.findById(objectId);
if (existingUser == null) {
throw new IllegalArgumentException("User not found");
}
// 如果更新邮箱,检查新邮箱是否已被其他用户使用
if (email != null && !email.equals(existingUser.getEmail())) {
User userWithEmail = userDAO.findByEmail(email);
if (userWithEmail != null && !userWithEmail.getId().equals(objectId)) {
throw new IllegalArgumentException("Email already exists");
}
}
// 更新用户信息
if (name != null) existingUser.setName(name);
if (email != null) existingUser.setEmail(email);
if (age != null) existingUser.setAge(age);
return userDAO.updateUser(objectId, existingUser);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Invalid user ID format");
}
}
public boolean updateUserAge(String id, int newAge) {
try {
ObjectId objectId = new ObjectId(id);
if (newAge < 0 || newAge > 150) {
throw new IllegalArgumentException("Age must be between 0 and 150");
}
return userDAO.updateAge(objectId, newAge);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Invalid user ID format");
}
}
// 删除用户
public boolean deleteUser(String id) {
try {
ObjectId objectId = new ObjectId(id);
// 检查用户是否存在
User existingUser = userDAO.findById(objectId);
if (existingUser == null) {
throw new IllegalArgumentException("User not found");
}
return userDAO.deleteUser(objectId);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Invalid user ID format");
}
}
// 统计操作
public long getUserCount() {
return userDAO.countUsers();
}
public long getAdultUserCount() {
return userDAO.countUsersByAge(18);
}
// 批量操作
public List<ObjectId> createUsers(List<User> users) {
// 验证所有用户数据
for (User user : users) {
if (user.getName() == null || user.getName().trim().isEmpty()) {
throw new IllegalArgumentException("All users must have a name");
}
if (user.getEmail() == null || !isValidEmail(user.getEmail())) {
throw new IllegalArgumentException("All users must have a valid email");
}
}
return userDAO.createUsers(users);
}
public long bulkDeleteUsersByAge(int maxAge) {
if (maxAge < 0) {
throw new IllegalArgumentException("Age must be non-negative");
}
return userDAO.deleteUsersByAge(maxAge);
}
// 邮箱格式验证
private boolean isValidEmail(String email) {
return email != null && email.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$");
}
}
8. 高级查询操作
8.1 复杂查询实现
package com.example.dao;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.*;
import org.bson.Document;
import org.bson.conversions.Bson;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
public class AdvancedQueryDAO {
private final MongoCollection<Document> collection;
public AdvancedQueryDAO(MongoCollection<Document> collection) {
this.collection = collection;
}
// 复合条件查询
public List<Document> findUsersWithComplexConditions() {
List<Document> results = new ArrayList<>();
// 年龄在20-40之间,且名字包含"John"或邮箱以"gmail"结尾
Bson ageFilter = Filters.and(
Filters.gte("age", 20),
Filters.lte("age", 40)
);
Bson nameOrEmailFilter = Filters.or(
Filters.regex("name", ".*John.*", "i"),
Filters.regex("email", ".*@gmail\\.com$", "i")
);
Bson complexFilter = Filters.and(ageFilter, nameOrEmailFilter);
for (Document doc : collection.find(complexFilter)) {
results.add(doc);
}
return results;
}
// 数组查询
public List<Document> findUsersByTags(String... tags) {
List<Document> results = new ArrayList<>();
// 包含所有指定标签的用户
Bson filter = Filters.all("tags", Arrays.asList(tags));
for (Document doc : collection.find(filter)) {
results.add(doc);
}
return results;
}
public List<Document> findUsersByAnyTag(String... tags) {
List<Document> results = new ArrayList<>();
// 包含任意一个指定标签的用户
Bson filter = Filters.in("tags", Arrays.asList(tags));
for (Document doc : collection.find(filter)) {
results.add(doc);
}
return results;
}
// 嵌套文档查询
public List<Document> findUsersByCity(String city) {
List<Document> results = new ArrayList<>();
Bson filter = Filters.eq("address.city", city);
for (Document doc : collection.find(filter)) {
results.add(doc);
}
return results;
}
// 存在性查询
public List<Document> findUsersWithAddress() {
List<Document> results = new ArrayList<>();
Bson filter = Filters.exists("address");
for (Document doc : collection.find(filter)) {
results.add(doc);
}
return results;
}
public List<Document> findUsersWithoutTags() {
List<Document> results = new ArrayList<>();
Bson filter = Filters.or(
Filters.exists("tags", false),
Filters.size("tags", 0)
);
for (Document doc : collection.find(filter)) {
results.add(doc);
}
return results;
}
// 正则表达式查询
public List<Document> findUsersByEmailDomain(String domain) {
List<Document> results = new ArrayList<>();
Pattern pattern = Pattern.compile(".*@" + Pattern.quote(domain) + "$", Pattern.CASE_INSENSITIVE);
Bson filter = Filters.regex("email", pattern);
for (Document doc : collection.find(filter)) {
results.add(doc);
}
return results;
}
// 类型查询
public List<Document> findUsersWithStringAge() {
List<Document> results = new ArrayList<>();
Bson filter = Filters.type("age", "string");
for (Document doc : collection.find(filter)) {
results.add(doc);
}
return results;
}
// 地理位置查询(需要创建地理索引)
public List<Document> findUsersNearLocation(double longitude, double latitude, double maxDistance) {
List<Document> results = new ArrayList<>();
Bson filter = Filters.near("location", longitude, latitude, maxDistance, null);
for (Document doc : collection.find(filter)) {
results.add(doc);
}
return results;
}
// 排序和限制
public List<Document> findTopUsersByAge(int limit) {
List<Document> results = new ArrayList<>();
for (Document doc : collection.find()
.sort(Sorts.descending("age"))
.limit(limit)) {
results.add(doc);
}
return results;
}
// 多字段排序
public List<Document> findUsersOrderedByAgeAndName() {
List<Document> results = new ArrayList<>();
Bson sort = Sorts.orderBy(
Sorts.ascending("age"),
Sorts.ascending("name")
);
for (Document doc : collection.find().sort(sort)) {
results.add(doc);
}
return results;
}
}
9. 索引操作
9.1 索引管理类
package com.example.index;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.IndexOptions;
import com.mongodb.client.model.Indexes;
import org.bson.Document;
import java.util.concurrent.TimeUnit;
public class IndexManager {
private final MongoCollection<Document> collection;
public IndexManager(MongoCollection<Document> collection) {
this.collection = collection;
}
// 创建单字段索引
public void createSingleFieldIndex() {
// 在 email 字段上创建升序索引
collection.createIndex(Indexes.ascending("email"));
System.out.println("Created ascending index on email field");
// 在 age 字段上创建降序索引
collection.createIndex(Indexes.descending("age"));
System.out.println("Created descending index on age field");
}
// 创建唯一索引
public void createUniqueIndex() {
IndexOptions indexOptions = new IndexOptions().unique(true);
collection.createIndex(Indexes.ascending("email"), indexOptions);
System.out.println("Created unique index on email field");
}
// 创建复合索引
public void createCompoundIndex() {
collection.createIndex(Indexes.compoundIndex(
Indexes.ascending("age"),
Indexes.descending("createdAt")
));
System.out.println("Created compound index on age (asc) and createdAt (desc)");
}
// 创建文本索引
public void createTextIndex() {
collection.createIndex(Indexes.compoundIndex(
Indexes.text("name"),
Indexes.text("email")
));
System.out.println("Created text index on name and email fields");
}
// 创建部分索引
public void createPartialIndex() {
IndexOptions indexOptions = new IndexOptions()
.partialFilterExpression(new Document("age", new Document("$gte", 18)));
collection.createIndex(Indexes.ascending("email"), indexOptions);
System.out.println("Created partial index on email for users >= 18 years old");
}
// 创建 TTL 索引(生存时间索引)
public void createTTLIndex() {
IndexOptions indexOptions = new IndexOptions()
.expireAfter(30L, TimeUnit.DAYS);
collection.createIndex(Indexes.ascending("createdAt"), indexOptions);
System.out.println("Created TTL index on createdAt field (30 days)");
}
// 创建稀疏索引
public void createSparseIndex() {
IndexOptions indexOptions = new IndexOptions().sparse(true);
collection.createIndex(Indexes.ascending("phoneNumber"), indexOptions);
System.out.println("Created sparse index on phoneNumber field");
}
// 创建地理空间索引
public void createGeospatialIndex() {
collection.createIndex(Indexes.geo2dsphere("location"));
System.out.println("Created 2dsphere index on location field");
}
// 创建哈希索引
public void createHashedIndex() {
collection.createIndex(Indexes.hashed("userId"));
System.out.println("Created hashed index on userId field");
}
// 列出所有索引
public void listAllIndexes() {
System.out.println("Existing indexes:");
for (Document index : collection.listIndexes()) {
System.out.println(index.toJson());
}
}
// 删除索引
public void dropIndexes() {
// 删除特定索引
collection.dropIndex("email_1");
System.out.println("Dropped index on email field");
// 删除所有索引(除了 _id 索引)
// collection.dropIndexes();
}
// 获取索引统计信息
public void getIndexStats() {
// 这需要通过聚合管道来获取
System.out.println("Index statistics would be retrieved through aggregation pipeline");
}
}
10. 聚合管道
10.1 聚合操作实现
package com.example.aggregation;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.AggregateIterable;
import com.mongodb.client.model.Aggregates;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Sorts;
import org.bson.Document;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class AggregationOperations {
private final MongoCollection<Document> collection;
public AggregationOperations(MongoCollection<Document> collection) {
this.collection = collection;
}
// 基本统计聚合
public Document getUserStatistics() {
List<Document> pipeline = Arrays.asList(
new Document("$group", new Document("_id", null)
.append("totalUsers", new Document("$sum", 1))
.append("averageAge", new Document("$avg", "$age"))
.append("minAge", new Document("$min", "$age"))
.append("maxAge", new Document("$max", "$age"))
.append("totalAge", new Document("$sum", "$age"))
)
);
AggregateIterable<Document> result = collection.aggregate(pipeline);
return result.first();
}
// 按年龄分组统计
public List<Document> getUsersByAgeGroup() {
List<Document> results = new ArrayList<>();
List<Document> pipeline = Arrays.asList(
new Document("$group", new Document("_id",
new Document("$switch", new Document("branches", Arrays.asList(
new Document("case", new Document("$lt", Arrays.asList("$age", 18)))
.append("then", "未成年"),
new Document("case", new Document("$lt", Arrays.asList("$age", 35)))
.append("then", "青年"),
new Document("case", new Document("$lt", Arrays.asList("$age", 60)))
.append("then", "中年")
)).append("default", "老年"))
).append("count", new Document("$sum", 1))
.append("averageAge", new Document("$avg", "$age"))),
new Document("$sort", new Document("count", -1))
);
for (Document doc : collection.aggregate(pipeline)) {
results.add(doc);
}
return results;
}
// 按城市分组统计
public List<Document> getUsersByCity() {
List<Document> results = new ArrayList<>();
List<Document> pipeline = Arrays.asList(
new Document("$match", new Document("address.city", new Document("$exists", true))),
new Document("$group", new Document("_id", "$address.city")
.append("userCount", new Document("$sum", 1))
.append("averageAge", new Document("$avg", "$age"))
.append("users", new Document("$push", new Document("name", "$name").append("email", "$email")))
),
new Document("$sort", new Document("userCount", -1))
);
for (Document doc : collection.aggregate(pipeline)) {
results.add(doc);
}
return results;
}
// 投影操作 - 重构输出格式
public List<Document> getUserProfiles() {
List<Document> results = new ArrayList<>();
List<Document> pipeline = Arrays.asList(
new Document("$project", new Document("_id", 0)
.append("fullName", "$name")
.append("contactInfo", new Document("email", "$email")
.append("phone", "$phoneNumber"))
.append("demographics", new Document("age", "$age")
.append("ageGroup", new Document("$switch", new Document("branches", Arrays.asList(
new Document("case", new Document("$lt", Arrays.asList("$age", 25)))
.append("then", "Young"),
new Document("case", new Document("$lt", Arrays.asList("$age", 50)))
.append("then", "Middle-aged")
)).append("default", "Senior"))))
.append("location", "$address.city")
.append("isActive", new Document("$cond", Arrays.asList(
new Document("$ne", Arrays.asList("$lastLoginDate", null)),
true,
false
)))
)
);
for (Document doc : collection.aggregate(pipeline)) {
results.add(doc);
}
return results;
}
// 查找操作 - 关联其他集合
public List<Document> getUsersWithOrderInfo(MongoCollection<Document> ordersCollection) {
List<Document> results = new ArrayList<>();
List<Document> pipeline = Arrays.asList(
new Document("$lookup", new Document("from", "orders")
.append("localField", "_id")
.append("foreignField", "userId")
.append("as", "orders")
),
new Document("$addFields", new Document("orderCount", new Document("$size", "$orders"))
.append("totalOrderValue", new Document("$sum", "$orders.amount"))
),
new Document("$match", new Document("orderCount", new Document("$gt", 0))),
new Document("$sort", new Document("totalOrderValue", -1))
);
for (Document doc : collection.aggregate(pipeline)) {
results.add(doc);
}
return results;
}
// 解构数组操作
public List<Document> getUserTagAnalysis() {
List<Document> results = new ArrayList<>();
List<Document> pipeline = Arrays.asList(
new Document("$match", new Document("tags", new Document("$exists", true))),
new Document("$unwind", "$tags"),
new Document("$group", new Document("_id", "$tags")
.append("userCount", new Document("$sum", 1))
.append("users", new Document("$push", "$name"))
),
new Document("$sort", new Document("userCount", -1)),
new Document("$limit", 10)
);
for (Document doc : collection.aggregate(pipeline)) {
results.add(doc);
}
return results;
}
// 时间序列分析
public List<Document> getUserRegistrationTrend() {
List<Document> results = new ArrayList<>();
List<Document> pipeline = Arrays.asList(
new Document("$group", new Document("_id", new Document("year", new Document("$year", "$createdAt"))
.append("month", new Document("$month", "$createdAt")))
.append("registrations", new Document("$sum", 1))
.append("averageAge", new Document("$avg", "$age"))
),
new Document("$sort", new Document("_id.year", 1).append("_id.month", 1)),
new Document("$project", new Document("_id", 0)
.append("period", new Document("$concat", Arrays.asList(
new Document("$toString", "$_id.year"),
"-",
new Document("$toString", "$_id.month")
)))
.append("registrations", 1)
.append("averageAge", new Document("$round", Arrays.asList("$averageAge", 1)))
)
);
for (Document doc : collection.aggregate(pipeline)) {
results.add(doc);
}
return results;
}
// 复杂的多阶段聚合
public List<Document> getAdvancedUserAnalytics() {
List<Document> results = new ArrayList<>();
List<Document> pipeline = Arrays.asList(
// 阶段 1: 匹配活跃用户
new Document("$match", new Document("isActive", true)),
// 阶段 2: 添加计算字段
new Document("$addFields", new Document("ageGroup",
new Document("$switch", new Document("branches", Arrays.asList(
new Document("case", new Document("$lt", Arrays.asList("$age", 25)))
.append("then", "18-24"),
new Document("case", new Document("$lt", Arrays.asList("$age", 35)))
.append("then", "25-34"),
new Document("case", new Document("$lt", Arrays.asList("$age", 50)))
.append("then", "35-49")
)).append("default", "50+"))
).append("emailDomain", new Document("$arrayElemAt", Arrays.asList(
new Document("$split", Arrays.asList("$email", "@")), 1
)))),
// 阶段 3: 按年龄组和邮箱域名分组
new Document("$group", new Document("_id", new Document("ageGroup", "$ageGroup")
.append("emailDomain", "$emailDomain"))
.append("userCount", new Document("$sum", 1))
.append("names", new Document("$push", "$name"))
),
// 阶段 4: 重新分组以获得每个年龄组的信息
new Document("$group", new Document("_id", "$_id.ageGroup")
.append("totalUsers", new Document("$sum", "$userCount"))
.append("domains", new Document("$push", new Document("domain", "$_id.emailDomain")
.append("count", "$userCount")))
),
// 阶段 5: 排序
new Document("$sort", new Document("totalUsers", -1)),
// 阶段 6: 投影最终结果
new Document("$project", new Document("_id", 0)
.append("ageGroup", "$_id")
.append("totalUsers", 1)
.append("topDomains", new Document("$slice", Arrays.asList(
new Document("$sortArray", new Document("input", "$domains")
.append("sortBy", new Document("count", -1))), 3
)))
)
);
for (Document doc : collection.aggregate(pipeline)) {
results.add(doc);
}
return results;
}
}
11. 事务处理
11.1 事务操作实现
package com.example.transaction;
import com.mongodb.client.ClientSession;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Updates;
import org.bson.Document;
import org.bson.types.ObjectId;
public class TransactionManager {
private final MongoClient mongoClient;
private final MongoDatabase database;
public TransactionManager(MongoClient mongoClient, MongoDatabase database) {
this.mongoClient = mongoClient;
this.database = database;
}
// 简单事务示例:转账操作
public boolean transferMoney(ObjectId fromUserId, ObjectId toUserId, double amount) {
ClientSession session = mongoClient.startSession();
try {
return session.withTransaction(() -> {
MongoCollection<Document> accounts = database.getCollection("accounts");
// 检查转出账户余额
Document fromAccount = accounts.find(session, Filters.eq("_id", fromUserId)).first();
if (fromAccount == null) {
throw new RuntimeException("转出账户不存在");
}
double fromBalance = fromAccount.getDouble("balance");
if (fromBalance < amount) {
throw new RuntimeException("余额不足");
}
// 检查转入账户是否存在
Document toAccount = accounts.find(session, Filters.eq("_id", toUserId)).first();
if (toAccount == null) {
throw new RuntimeException("转入账户不存在");
}
// 执行转账
accounts.updateOne(session,
Filters.eq("_id", fromUserId),
Updates.inc("balance", -amount)
);
accounts.updateOne(session,
Filters.eq("_id", toUserId),
Updates.inc("balance", amount)
);
// 记录交易日志
MongoCollection<Document> transactions = database.getCollection("transactions");
Document transaction = new Document()
.append("fromUserId", fromUserId)
.append("toUserId", toUserId)
.append("amount", amount)
.append("timestamp", System.currentTimeMillis())
.append("status", "completed");
transactions.insertOne(session, transaction);
return true;
});
} catch (Exception e) {
System.err.println("转账失败: " + e.getMessage());
return false;
} finally {
session.close();
}
}
// 复杂事务示例:创建订单
public ObjectId createOrderWithInventoryUpdate(ObjectId userId, String productId, int quantity) {
ClientSession session = mongoClient.startSession();
try {
return session.withTransaction(() -> {
MongoCollection<Document> users = database.getCollection("users");
MongoCollection<Document> products = database.getCollection("products");
MongoCollection<Document> orders = database.getCollection("orders");
MongoCollection<Document> inventory = database.getCollection("inventory");
// 1. 验证用户存在
Document user = users.find(session, Filters.eq("_id", userId)).first();
if (user == null) {
throw new RuntimeException("用户不存在");
}
// 2. 验证产品存在并获取价格
Document product = products.find(session, Filters.eq("productId", productId)).first();
if (product == null) {
throw new RuntimeException("产品不存在");
}
double price = product.getDouble("price");
// 3. 检查库存并更新
Document inventoryDoc = inventory.find(session, Filters.eq("productId", productId)).first();
if (inventoryDoc == null) {
throw new RuntimeException("库存记录不存在");
}
int currentStock = inventoryDoc.getInteger("quantity");
if (currentStock < quantity) {
throw new RuntimeException("库存不足");
}
// 更新库存
inventory.updateOne(session,
Filters.eq("productId", productId),
Updates.inc("quantity", -quantity)
);
// 4. 创建订单
ObjectId orderId = new ObjectId();
Document order = new Document()
.append("_id", orderId)
.append("userId", userId)
.append("productId", productId)
.append("quantity", quantity)
.append("unitPrice", price)
.append("totalAmount", price * quantity)
.append("status", "pending")
.append("createdAt", System.currentTimeMillis());
orders.insertOne(session, order);
// 5. 更新用户订单历史
users.updateOne(session,
Filters.eq("_id", userId),
Updates.push("orderHistory", orderId)
);
return orderId;
});
} catch (Exception e) {
System.err.println("创建订单失败: " + e.getMessage());
return null;
} finally {
session.close();
}
}
// 回调式事务处理
public boolean performMultiCollectionOperation() {
ClientSession session = mongoClient.startSession();
try {
session.withTransaction(() -> {
MongoCollection<Document> collection1 = database.getCollection("collection1");
MongoCollection<Document> collection2 = database.getCollection("collection2");
// 在事务中执行多个操作
collection1.insertOne(session, new Document("field1", "value1"));
collection2.updateOne(session,
Filters.eq("_id", "someId"),
Updates.set("field2", "value2")
);
// 如果这里抛出异常,整个事务会回滚
// throw new RuntimeException("模拟失败");
return "success";
});
return true;
} catch (Exception e) {
System.err.println("事务执行失败: " + e.getMessage());
return false;
} finally {
session.close();
}
}
// 手动事务控制
public boolean manualTransactionControl() {
ClientSession session = mongoClient.startSession();
try {
session.startTransaction();
MongoCollection<Document> users = database.getCollection("users");
MongoCollection<Document> logs = database.getCollection("logs");
try {
// 执行业务操作
users.insertOne(session, new Document("name", "New User"));
logs.insertOne(session, new Document("action", "user_created"));
// 手动提交事务
session.commitTransaction();
return true;
} catch (Exception e) {
// 手动回滚事务
session.abortTransaction();
System.err.println("事务回滚: " + e.getMessage());
return false;
}
} finally {
session.close();
}
}
}
12. Spring Boot 集成
12.1 Spring Boot 配置
// pom.xml 添加依赖
/*
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
*/
// application.yml
/*
spring:
data:
mongodb:
uri: mongodb://localhost:27017/myapp
# 或者分别配置
host: localhost
port: 27017
database: myapp
username: admin
password: password
authentication-database: admin
server:
port: 8080
logging:
level:
org.springframework.data.mongodb: DEBUG
*/
package com.example.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
@Configuration
@EnableMongoRepositories(basePackages = "com.example.repository")
public class MongoConfig extends AbstractMongoClientConfiguration {
@Override
protected String getDatabaseName() {
return "myapp";
}
// 自定义配置
/*
@Override
public MongoClient mongoClient() {
ConnectionString connectionString = new ConnectionString("mongodb://localhost:27017/myapp");
MongoClientSettings mongoClientSettings = MongoClientSettings.builder()
.applyConnectionString(connectionString)
.build();
return MongoClients.create(mongoClientSettings);
}
*/
}
12.2 Spring Data MongoDB 实体类
package com.example.entity;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.index.Indexed;
import java.time.LocalDateTime;
import java.util.List;
@Document(collection = "users")
public class User {
@Id
private String id;
@Field("full_name")
private String name;
@Indexed(unique = true)
private String email;
private Integer age;
private Address address;
private List<String> tags;
@CreatedDate
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime updatedAt;
// 构造函数
public User() {}
public User(String name, String email, Integer age) {
this.name = name;
this.email = email;
this.age = age;
}
// Getter 和 Setter 方法
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
public Address getAddress() { return address; }
public void setAddress(Address address) { this.address = address; }
public List<String> getTags() { return tags; }
public void setTags(List<String> tags) { this.tags = tags; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
public LocalDateTime getUpdatedAt() { return updatedAt; }
public void setUpdatedAt(LocalDateTime updatedAt) { this.updatedAt = updatedAt; }
}
@Document
class Address {
private String street;
private String city;
private String zipcode;
private String country;
// 构造函数和 Getter/Setter 方法
public Address() {}
public Address(String street, String city, String zipcode, String country) {
this.street = street;
this.city = city;
this.zipcode = zipcode;
this.country = country;
}
// Getter 和 Setter 方法省略...
}
12.3 Spring Data MongoDB Repository
package com.example.repository;
import com.example.entity.User;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.data.mongodb.repository.Aggregation;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface UserRepository extends MongoRepository<User, String> {
// 基本查询方法(Spring Data 自动实现)
Optional<User> findByEmail(String email);
List<User> findByName(String name);
List<User> findByAgeGreaterThan(Integer age);
List<User> findByAgeBetween(Integer minAge, Integer maxAge);
List<User> findByNameContainingIgnoreCase(String keyword);
List<User> findByAddressCity(String city);
List<User> findByTagsContaining(String tag);
// 分页查询
Page<User> findByAgeGreaterThan(Integer age, Pageable pageable);
// 排序查询
List<User> findByOrderByAgeDesc();
List<User> findByAgeBetweenOrderByNameAsc(Integer minAge, Integer maxAge);
// 自定义查询
@Query("{ 'age' : { $gte: ?0, $lte: ?1 } }")
List<User> findUsersInAgeRange(Integer minAge, Integer maxAge);
@Query("{ 'email' : { $regex: ?0, $options: 'i' } }")
List<User> findByEmailPattern(String pattern);
@Query(value = "{ 'age' : { $gte: ?0 } }", fields = "{ 'name' : 1, 'email' : 1 }")
List<User> findUserNamesAndEmailsByMinAge(Integer minAge);
// 删除查询
Long deleteByAge(Integer age);
Long deleteByAgeGreaterThan(Integer age);
// 聚合查询
@Aggregation(pipeline = {
"{ $group: { _id: null, totalUsers: { $sum: 1 }, averageAge: { $avg: '$age' } } }"
})
UserStatistics getUserStatistics();
@Aggregation(pipeline = {
"{ $group: { _id: '$address.city', count: { $sum: 1 } } }",
"{ $sort: { count: -1 } }"
})
List<CityUserCount> getUserCountByCity();
// 统计接口
interface UserStatistics {
Integer getTotalUsers();
Double getAverageAge();
}
interface CityUserCount {
String getId(); // city name
Integer getCount();
}
}
12.4 Service 层(Spring Boot)
package com.example.service;
import com.example.entity.User;
import com.example.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Optional;
@Service
@Transactional
public class UserService {
@Autowired
private UserRepository userRepository;
// 创建用户
public User createUser(User user) {
// 业务逻辑验证
validateUser(user);
// 检查邮箱是否已存在
Optional<User> existingUser = userRepository.findByEmail(user.getEmail());
if (existingUser.isPresent()) {
throw new IllegalArgumentException("Email already exists");
}
return userRepository.save(user);
}
// 批量创建用户
public List<User> createUsers(List<User> users) {
// 验证所有用户
users.forEach(this::validateUser);
return userRepository.saveAll(users);
}
// 获取用户
public Optional<User> getUserById(String id) {
return userRepository.findById(id);
}
public Optional<User> getUserByEmail(String email) {
return userRepository.findByEmail(email);
}
public List<User> getAllUsers() {
return userRepository.findAll();
}
// 分页查询
public Page<User> getUsersWithPagination(int page, int size, String sortBy, String sortDir) {
Sort sort = sortDir.equalsIgnoreCase("desc") ?
Sort.by(sortBy).descending() : Sort.by(sortBy).ascending();
Pageable pageable = PageRequest.of(page, size, sort);
return userRepository.findAll(pageable);
}
// 条件查询
public List<User> getUsersByAgeRange(Integer minAge, Integer maxAge) {
return userRepository.findByAgeBetween(minAge, maxAge);
}
public List<User> searchUsersByName(String keyword) {
return userRepository.findByNameContainingIgnoreCase(keyword);
}
public List<User> getUsersByCity(String city) {
return userRepository.findByAddressCity(city);
}
public List<User> getUsersByTag(String tag) {
return userRepository.findByTagsContaining(tag);
}
// 更新用户
public User updateUser(String id, User updatedUser) {
return userRepository.findById(id)
.map(existingUser -> {
existingUser.setName(updatedUser.getName());
existingUser.setEmail(updatedUser.getEmail());
existingUser.setAge(updatedUser.getAge());
existingUser.setAddress(updatedUser.getAddress());
existingUser.setTags(updatedUser.getTags());
return userRepository.save(existingUser);
})
.orElseThrow(() -> new IllegalArgumentException("User not found with id: " + id));
}
// 删除用户
public boolean deleteUser(String id) {
return userRepository.findById(id)
.map(user -> {
userRepository.delete(user);
return true;
})
.orElse(false);
}
public void deleteAllUsers() {
userRepository.deleteAll();
}
// 统计操作
public long getUserCount() {
return userRepository.count();
}
public UserRepository.UserStatistics getUserStatistics() {
return userRepository.getUserStatistics();
}
public List<UserRepository.CityUserCount> getUserCountByCity() {
return userRepository.getUserCountByCity();
}
// 业务逻辑验证
private void validateUser(User user) {
if (user.getName() == null || user.getName().trim().isEmpty()) {
throw new IllegalArgumentException("Name cannot be empty");
}
if (user.getEmail() == null || !isValidEmail(user.getEmail())) {
throw new IllegalArgumentException("Invalid email format");
}
if (user.getAge() != null && (user.getAge() < 0 || user.getAge() > 150)) {
throw new IllegalArgumentException("Age must be between 0 and 150");
}
}
private boolean isValidEmail(String email) {
return email.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$");
}
}
12.5 Controller 层
package com.example.controller;
import com.example.entity.User;
import com.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
import java.util.Optional;
@RestController
@RequestMapping("/api/users")
@CrossOrigin(origins = "*")
public class UserController {
@Autowired
private UserService userService;
// 创建用户
@PostMapping
public ResponseEntity<User> createUser(@Valid @RequestBody User user) {
try {
User createdUser = userService.createUser(user);
return new ResponseEntity<>(createdUser, HttpStatus.CREATED);
} catch (IllegalArgumentException e) {
return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
}
}
// 批量创建用户
@PostMapping("/batch")
public ResponseEntity<List<User>> createUsers(@Valid @RequestBody List<User> users) {
try {
List<User> createdUsers = userService.createUsers(users);
return new ResponseEntity<>(createdUsers, HttpStatus.CREATED);
} catch (IllegalArgumentException e) {
return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
}
}
// 获取所有用户
@GetMapping
public ResponseEntity<List<User>> getAllUsers() {
List<User> users = userService.getAllUsers();
return new ResponseEntity<>(users, HttpStatus.OK);
}
// 分页获取用户
@GetMapping("/page")
public ResponseEntity<Page<User>> getUsersWithPagination(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(defaultValue = "id") String sortBy,
@RequestParam(defaultValue = "asc") String sortDir) {
Page<User> users = userService.getUsersWithPagination(page, size, sortBy, sortDir);
return new ResponseEntity<>(users, HttpStatus.OK);
}
// 根据ID获取用户
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable String id) {
Optional<User> user = userService.getUserById(id);
return user.map(u -> new ResponseEntity<>(u, HttpStatus.OK))
.orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
// 根据邮箱获取用户
@GetMapping("/email/{email}")
public ResponseEntity<User> getUserByEmail(@PathVariable String email) {
Optional<User> user = userService.getUserByEmail(email);
return user.map(u -> new ResponseEntity<>(u, HttpStatus.OK))
.orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND));
}
// 条件查询
@GetMapping("/age-range")
public ResponseEntity<List<User>> getUsersByAgeRange(
@RequestParam Integer minAge,
@RequestParam Integer maxAge) {
List<User> users = userService.getUsersByAgeRange(minAge, maxAge);
return new ResponseEntity<>(users, HttpStatus.OK);
}
@GetMapping("/search")
public ResponseEntity<List<User>> searchUsers(@RequestParam String keyword) {
List<User> users = userService.searchUsersByName(keyword);
return new ResponseEntity<>(users, HttpStatus.OK);
}
@GetMapping("/city/{city}")
public ResponseEntity<List<User>> getUsersByCity(@PathVariable String city) {
List<User> users = userService.getUsersByCity(city);
return new ResponseEntity<>(users, HttpStatus.OK);
}
@GetMapping("/tag/{tag}")
public ResponseEntity<List<User>> getUsersByTag(@PathVariable String tag) {
List<User> users = userService.getUsersByTag(tag);
return new ResponseEntity<>(users, HttpStatus.OK);
}
// 更新用户
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable String id, @Valid @RequestBody User user) {
try {
User updatedUser = userService.updateUser(id, user);
return new ResponseEntity<>(updatedUser, HttpStatus.OK);
} catch (IllegalArgumentException e) {
return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
}
}
// 删除用户
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable String id) {
boolean deleted = userService.deleteUser(id);
return deleted ?
new ResponseEntity<>(HttpStatus.NO_CONTENT) :
new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
// 统计信息
@GetMapping("/count")
public ResponseEntity<Long> getUserCount() {
long count = userService.getUserCount();
return new ResponseEntity<>(count, HttpStatus.OK);
}
@GetMapping("/statistics")
public ResponseEntity<UserService.UserRepository.UserStatistics> getUserStatistics() {
UserService.UserRepository.UserStatistics stats = userService.getUserStatistics();
return new ResponseEntity<>(stats, HttpStatus.OK);
}
@GetMapping("/city-stats")
public ResponseEntity<List<UserService.UserRepository.CityUserCount>> getCityStats() {
List<UserService.UserRepository.CityUserCount> stats = userService.getUserCountByCity();
return new ResponseEntity<>(stats, HttpStatus.OK);
}
}
13. 最佳实践
13.1 性能优化
package com.example.optimization;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.IndexOptions;
import com.mongodb.client.model.Indexes;
import org.bson.Document;
public class PerformanceOptimization {
private final MongoCollection<Document> collection;
public PerformanceOptimization(MongoCollection<Document> collection) {
this.collection = collection;
}
// 1. 创建合适的索引
public void createOptimizedIndexes() {
// 单字段索引
collection.createIndex(Indexes.ascending("email"));
collection.createIndex(Indexes.ascending("age"));
// 复合索引 - 注意字段顺序
collection.createIndex(Indexes.compoundIndex(
Indexes.ascending("age"), // 选择性高的字段放前面
Indexes.ascending("city"),
Indexes.descending("createdAt")
));
// 文本索引
collection.createIndex(Indexes.text("name"));
// 部分索引 - 只索引满足条件的文档
IndexOptions partialOptions = new IndexOptions()
.partialFilterExpression(new Document("age", new Document("$gte", 18)));
collection.createIndex(Indexes.ascending("email"), partialOptions);
}
// 2. 查询优化示例
public void optimizedQueries() {
// 使用投影减少网络传输
collection.find()
.projection(new Document("name", 1).append("email", 1))
.forEach(doc -> System.out.println(doc.toJson()));
// 使用 limit 限制结果集
collection.find()
.limit(10)
.forEach(doc -> System.out.println(doc.toJson()));
// 使用 hint 强制使用特定索引
collection.find(new Document("age", new Document("$gte", 18)))
.hint(new Document("age", 1))
.forEach(doc -> System.out.println(doc.toJson()));
}
// 3. 批量操作优化
public void optimizedBulkOperations() {
// 使用批量写入而不是多次单独写入
// ... 批量操作代码
}
}
13.2 错误处理和日志
package com.example.exception;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<ErrorResponse> handleIllegalArgument(IllegalArgumentException e) {
logger.warn("Invalid argument: {}", e.getMessage());
return new ResponseEntity<>(
new ErrorResponse("INVALID_ARGUMENT", e.getMessage()),
HttpStatus.BAD_REQUEST
);
}
@ExceptionHandler(RuntimeException.class)
public ResponseEntity<ErrorResponse> handleRuntimeException(RuntimeException e) {
logger.error("Runtime exception occurred", e);
return new ResponseEntity<>(
new ErrorResponse("INTERNAL_ERROR", "An internal error occurred"),
HttpStatus.INTERNAL_SERVER_ERROR
);
}
public static class ErrorResponse {
private String code;
private String message;
public ErrorResponse(String code, String message) {
this.code = code;
this.message = message;
}
// Getter 方法
public String getCode() { return code; }
public String getMessage() { return message; }
}
}
13.3 主应用程序类
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.mongodb.config.EnableMongoAuditing;
@SpringBootApplication
@EnableMongoAuditing
public class MongoDBApplication {
public static void main(String[] args) {
SpringApplication.run(MongoDBApplication.class, args);
System.out.println("MongoDB Spring Boot Application Started!");
}
}
13.4 测试类示例
package com.example;
import com.example.entity.User;
import com.example.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
@TestPropertySource(properties = {
"spring.data.mongodb.database=test_db"
})
public class UserServiceTest {
@Autowired
private UserService userService;
@Test
public void testCreateUser() {
User user = new User("Test User", "test@example.com", 25);
User createdUser = userService.createUser(user);
assertNotNull(createdUser.getId());
assertEquals("Test User", createdUser.getName());
assertEquals("test@example.com", createdUser.getEmail());
assertEquals(25, createdUser.getAge());
}
@Test
public void testFindUserByEmail() {
// 先创建一个用户
User user = new User("Find Test", "findtest@example.com", 30);
userService.createUser(user);
// 然后查找
var foundUser = userService.getUserByEmail("findtest@example.com");
assertTrue(foundUser.isPresent());
assertEquals("Find Test", foundUser.get().getName());
}
}
这个完整指南涵盖了 MongoDB 与 Java 集成的所有重要方面,从基础安装配置到高级的聚合操作和 Spring Boot 集成。每个部分都包含详细的代码示例和解释,可以作为开发过程中的参考手册。