Spring Boot中MongoTemplate从入门到实战深度解析
作者:醉风塘
本文介绍了Spring Data MongoDB中的MongoTemplate,包括主要特性、核心配置、基础CRUD、高级查询与聚合操作、GridFS操作、性能优化与监控以及最佳实践,通过全面的示例项目,展示了如何在Spring Boot应用中使用MongoTemplate进行数据库操作,感兴趣的朋友跟随小编一起看看吧
一、MongoTemplate概述
1.1 什么是MongoTemplate
MongoTemplate是Spring Data MongoDB提供的核心类,它封装了MongoDB的Java驱动,提供了丰富的方法来操作MongoDB数据库。相比直接使用MongoDB驱动,MongoTemplate提供了更简洁、更符合Spring风格的API。
主要特性:
- 丰富的CRUD操作
- 强大的查询构建器
- 聚合框架支持
- 网格文件系统(GridFS)支持
- 声明式事务支持
- 自动类型转换
1.2 核心依赖配置
<!-- pom.xml -->
<dependencies>
<!-- Spring Boot Starter Data MongoDB -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<!-- 如果需要Reactive支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>
<!-- Lombok简化代码 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>1.3 配置文件
# application.yml
spring:
data:
mongodb:
# 单机配置
host: localhost
port: 27017
database: testdb
username: admin
password: secret
# 连接池配置
auto-index-creation: true
authentication-database: admin
# 副本集配置
# uri: mongodb://user:pass@host1:port1,host2:port2/database?replicaSet=rs0
# 连接超时配置
connection-per-host: 100
min-connections-per-host: 10
max-wait-time: 120000
connect-timeout: 10000
socket-timeout: 0
# SSL配置
ssl:
enabled: false
# 监控配置
mongodb:
metrics:
enabled: true二、MongoTemplate核心配置
2.1 基础配置类
@Configuration
@EnableMongoAuditing // 启用审计功能
public class MongoConfig {
@Value("${spring.data.mongodb.database}")
private String database;
@Value("${spring.data.mongodb.host}")
private String host;
@Value("${spring.data.mongodb.port}")
private int port;
/**
* 方式一:使用属性配置创建MongoClient
*/
@Bean
public MongoClient mongoClient() {
ConnectionString connectionString = new ConnectionString(
String.format("mongodb://%s:%d/%s", host, port, database)
);
MongoClientSettings settings = MongoClientSettings.builder()
.applyConnectionString(connectionString)
.applyToConnectionPoolSettings(builder ->
builder.maxSize(100) // 连接池最大连接数
.minSize(10) // 最小连接数
.maxWaitTime(2, TimeUnit.MINUTES) // 最大等待时间
)
.applyToSocketSettings(builder ->
builder.connectTimeout(10, TimeUnit.SECONDS) // 连接超时
.readTimeout(30, TimeUnit.SECONDS) // 读取超时
)
.applyToSslSettings(builder ->
builder.enabled(false) // 禁用SSL
)
.codecRegistry(CodecRegistries.fromRegistries(
MongoClientSettings.getDefaultCodecRegistry(),
CodecRegistries.fromProviders(PojoCodecProvider.builder()
.automatic(true)
.build())
))
.build();
return MongoClients.create(settings);
}
/**
* 方式二:使用MongoTemplate Bean
*/
@Bean
public MongoTemplate mongoTemplate(MongoClient mongoClient) {
return new MongoTemplate(mongoClient, database);
}
/**
* 配置自定义转换器
*/
@Bean
public MongoCustomConversions customConversions() {
List<Converter<?, ?>> converters = new ArrayList<>();
converters.add(new LocalDateTimeToStringConverter());
converters.add(new StringToLocalDateTimeConverter());
converters.add(new BigDecimalToDecimal128Converter());
converters.add(new Decimal128ToBigDecimalConverter());
return new MongoCustomConversions(converters);
}
/**
* 自定义类型映射
*/
@Bean
public MappingMongoConverter mappingMongoConverter(
MongoDatabaseFactory factory,
MongoMappingContext context,
MongoCustomConversions conversions) {
DbRefResolver dbRefResolver = new DefaultDbRefResolver(factory);
MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, context);
converter.setCustomConversions(conversions);
converter.setTypeMapper(new DefaultMongoTypeMapper(null)); // 不保存_class字段
// 配置字段映射策略
converter.setMapKeyDotReplacement("_"); // 将Map key中的点替换为下划线
return converter;
}
/**
* 自定义转换器示例
*/
public class LocalDateTimeToStringConverter implements Converter<LocalDateTime, String> {
@Override
public String convert(LocalDateTime source) {
return source.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
}
public class StringToLocalDateTimeConverter implements Converter<String, LocalDateTime> {
@Override
public LocalDateTime convert(String source) {
return LocalDateTime.parse(source, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
}
}2.2 多数据源配置
@Configuration
public class MultiMongoConfig {
/**
* 主数据源配置
*/
@Configuration
@EnableMongoRepositories(
basePackages = "com.example.repository.primary",
mongoTemplateRef = "primaryMongoTemplate"
)
public static class PrimaryMongoConfig {
@Bean
@Primary
@ConfigurationProperties(prefix = "spring.data.mongodb.primary")
public MongoProperties primaryProperties() {
return new MongoProperties();
}
@Primary
@Bean(name = "primaryMongoClient")
public MongoClient primaryMongoClient() {
MongoProperties properties = primaryProperties();
return MongoClients.create(
MongoClientSettings.builder()
.applyConnectionString(new ConnectionString(properties.getUri()))
.build()
);
}
@Primary
@Bean(name = "primaryMongoTemplate")
public MongoTemplate primaryMongoTemplate() {
return new MongoTemplate(primaryMongoClient(),
primaryProperties().getDatabase());
}
}
/**
* 次数据源配置
*/
@Configuration
@EnableMongoRepositories(
basePackages = "com.example.repository.secondary",
mongoTemplateRef = "secondaryMongoTemplate"
)
public static class SecondaryMongoConfig {
@Bean
@ConfigurationProperties(prefix = "spring.data.mongodb.secondary")
public MongoProperties secondaryProperties() {
return new MongoProperties();
}
@Bean(name = "secondaryMongoClient")
public MongoClient secondaryMongoClient() {
MongoProperties properties = secondaryProperties();
return MongoClients.create(
MongoClientSettings.builder()
.applyConnectionString(new ConnectionString(properties.getUri()))
.build()
);
}
@Bean(name = "secondaryMongoTemplate")
public MongoTemplate secondaryMongoTemplate() {
return new MongoTemplate(secondaryMongoClient(),
secondaryProperties().getDatabase());
}
}
}2.3 实体类映射配置
/**
* 基础实体类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
@Document(collection = "base_entities")
@Inheritance // 支持继承
public abstract class BaseEntity {
@Id
private String id;
@CreatedDate
@Field("created_at")
private LocalDateTime createdAt;
@LastModifiedDate
@Field("updated_at")
private LocalDateTime updatedAt;
@CreatedBy
@Field("created_by")
private String createdBy;
@LastModifiedBy
@Field("updated_by")
private String updatedBy;
@Version
private Long version;
@Field("is_deleted")
private Boolean isDeleted = false;
/**
* 软删除标记
*/
public void softDelete() {
this.isDeleted = true;
this.updatedAt = LocalDateTime.now();
}
}
/**
* 用户实体类
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Document(collection = "users")
@CompoundIndexes({
@CompoundIndex(name = "idx_username_email", def = "{'username': 1, 'email': 1}"),
@CompoundIndex(name = "idx_created_at", def = "{'createdAt': -1}")
})
@TypeAlias("user") // 类型别名
public class User extends BaseEntity {
@Indexed(unique = true, background = true)
@Field("username")
private String username;
@Indexed
@Field("email")
@Email
private String email;
@Field("age")
@Min(0)
@Max(150)
private Integer age;
@Field("address")
private Address address;
@Field("roles")
private Set<String> roles = new HashSet<>();
@Field("metadata")
private Map<String, Object> metadata = new HashMap<>();
@Field("profile")
private UserProfile profile;
@Field("status")
@DBRef(lazy = true) // 懒加载引用
private Status status;
// 自定义转换器
@Field("custom_field")
@PersistenceConstructor
public User(String id, String username, String email) {
this.id = id;
this.username = username;
this.email = email;
}
}
/**
* 地址值对象
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Address {
@Field("street")
private String street;
@Field("city")
private String city;
@Field("postal_code")
private String postalCode;
@Field("country")
private String country;
// 地理空间数据
@GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE)
@Field("location")
private GeoJsonPoint location;
}
/**
* 审计配置
*/
@Configuration
public class AuditorConfig implements AuditorAware<String> {
@Override
public Optional<String> getCurrentAuditor() {
// 从安全上下文获取当前用户
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || !authentication.isAuthenticated()) {
return Optional.of("system");
}
return Optional.of(authentication.getName());
}
}三、基础CRUD操作
3.1 插入操作
@Service
@Slf4j
@Transactional
public class UserService {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 插入单个文档
*/
public User insertUser(User user) {
// 设置创建时间
user.setCreatedAt(LocalDateTime.now());
user.setUpdatedAt(LocalDateTime.now());
// 插入并返回
User savedUser = mongoTemplate.insert(user);
log.info("Inserted user: {}", savedUser.getId());
return savedUser;
}
/**
* 批量插入文档
*/
public List<User> batchInsertUsers(List<User> users) {
// 设置时间戳
users.forEach(user -> {
user.setCreatedAt(LocalDateTime.now());
user.setUpdatedAt(LocalDateTime.now());
});
// 批量插入
Collection<User> insertedUsers = mongoTemplate.insertAll(users);
log.info("Batch inserted {} users", insertedUsers.size());
return new ArrayList<>(insertedUsers);
}
/**
* 保存或更新(upsert)
*/
public User saveOrUpdateUser(User user) {
if (user.getId() == null) {
return insertUser(user);
} else {
return updateUser(user);
}
}
/**
* 使用BulkOperations批量操作
*/
public BulkWriteResult bulkInsertUsers(List<User> users) {
BulkOperations bulkOps = mongoTemplate.bulkOps(
BulkOperations.BulkMode.ORDERED, User.class);
List<InsertOneModel<User>> operations = users.stream()
.map(user -> {
user.setCreatedAt(LocalDateTime.now());
user.setUpdatedAt(LocalDateTime.now());
return new InsertOneModel<>(user);
})
.collect(Collectors.toList());
bulkOps.insert(users);
return bulkOps.execute();
}
}3.2 查询操作
@Service
@Slf4j
public class UserQueryService {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 基础查询方法
*/
// 根据ID查询
public User findById(String id) {
return mongoTemplate.findById(id, User.class);
}
// 查询所有
public List<User> findAll() {
return mongoTemplate.findAll(User.class);
}
// 查询所有(指定集合)
public List<User> findAllInCollection(String collectionName) {
return mongoTemplate.findAll(User.class, collectionName);
}
// 条件查询
public List<User> findByUsername(String username) {
Query query = Query.query(Criteria.where("username").is(username));
return mongoTemplate.find(query, User.class);
}
// 多条件查询
public List<User> findByCityAndAgeGreaterThan(String city, int minAge) {
Query query = Query.query(
Criteria.where("address.city").is(city)
.and("age").gte(minAge)
);
return mongoTemplate.find(query, User.class);
}
// 模糊查询
public List<User> findByNameLike(String namePattern) {
Query query = Query.query(
Criteria.where("username").regex(namePattern, "i") // 忽略大小写
);
return mongoTemplate.find(query, User.class);
}
// IN查询
public List<User> findByIds(List<String> ids) {
Query query = Query.query(Criteria.where("id").in(ids));
return mongoTemplate.find(query, User.class);
}
// NOT查询
public List<User> findByCityNot(String city) {
Query query = Query.query(Criteria.where("address.city").ne(city));
return mongoTemplate.find(query, User.class);
}
// NULL查询
public List<User> findUsersWithNullEmail() {
Query query = Query.query(Criteria.where("email").is(null));
return mongoTemplate.find(query, User.class);
}
// EXISTS查询
public List<User> findUsersWithAddress() {
Query query = Query.query(Criteria.where("address").exists(true));
return mongoTemplate.find(query, User.class);
}
/**
* 分页查询
*/
public Page<User> findUsersWithPagination(int page, int size, String sortField) {
Query query = new Query();
// 总数
long total = mongoTemplate.count(query, User.class);
// 分页
query.skip((long) (page - 1) * size)
.limit(size);
// 排序
if (StringUtils.isNotBlank(sortField)) {
query.with(Sort.by(Sort.Direction.ASC, sortField));
}
// 查询数据
List<User> content = mongoTemplate.find(query, User.class);
return new PageImpl<>(content, PageRequest.of(page - 1, size), total);
}
/**
* 投影查询(只返回指定字段)
*/
public List<User> findUsersWithProjection() {
Query query = new Query();
// 指定返回的字段
query.fields()
.include("username")
.include("email")
.include("address.city")
.exclude("id")
.exclude("createdAt")
.exclude("updatedAt");
return mongoTemplate.find(query, User.class);
}
/**
* 使用Distinct去重
*/
public List<String> findDistinctCities() {
return mongoTemplate.findDistinct(
new Query(), "address.city", User.class, String.class);
}
/**
* 地理空间查询
*/
public List<User> findUsersNearLocation(double longitude, double latitude, double maxDistanceInMeters) {
Query query = Query.query(
Criteria.where("address.location")
.near(new Point(longitude, latitude))
.maxDistance(maxDistanceInMeters)
);
return mongoTemplate.find(query, User.class);
}
/**
* 使用游标处理大数据集
*/
public void processLargeUserDataset() {
Query query = Query.query(Criteria.where("age").gte(18));
try (MongoCursor<User> cursor = mongoTemplate.stream(query, User.class)) {
while (cursor.hasNext()) {
User user = cursor.next();
// 处理每个用户
processUser(user);
}
}
}
/**
* 查询一个文档
*/
public User findOneUser(String username) {
Query query = Query.query(Criteria.where("username").is(username));
return mongoTemplate.findOne(query, User.class);
}
/**
* 判断文档是否存在
*/
public boolean exists(String username) {
Query query = Query.query(Criteria.where("username").is(username));
return mongoTemplate.exists(query, User.class);
}
/**
* 获取查询数量
*/
public long countUsersByCity(String city) {
Query query = Query.query(Criteria.where("address.city").is(city));
return mongoTemplate.count(query, User.class);
}
private void processUser(User user) {
// 处理逻辑
}
}3.3 更新操作
@Service
@Slf4j
@Transactional
public class UserUpdateService {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 更新整个文档
*/
public User updateUser(User user) {
user.setUpdatedAt(LocalDateTime.now());
return mongoTemplate.save(user);
}
/**
* 部分更新
*/
public UpdateResult updateUserEmail(String userId, String newEmail) {
Query query = Query.query(Criteria.where("id").is(userId));
Update update = Update.update("email", newEmail)
.set("updatedAt", LocalDateTime.now());
return mongoTemplate.updateFirst(query, update, User.class);
}
/**
* 更新多个文档
*/
public UpdateResult updateUsersAgeByCity(String city, int newAge) {
Query query = Query.query(Criteria.where("address.city").is(city));
Update update = Update.update("age", newAge)
.set("updatedAt", LocalDateTime.now());
return mongoTemplate.updateMulti(query, update, User.class);
}
/**
* Upsert操作(不存在则插入)
*/
public UpdateResult upsertUser(User user) {
Query query = Query.query(Criteria.where("username").is(user.getUsername()));
Update update = new Update()
.set("email", user.getEmail())
.set("age", user.getAge())
.set("updatedAt", LocalDateTime.now())
.setOnInsert("createdAt", LocalDateTime.now());
return mongoTemplate.upsert(query, update, User.class);
}
/**
* 原子操作:递增字段
*/
public UpdateResult incrementUserAge(String userId, int increment) {
Query query = Query.query(Criteria.where("id").is(userId));
Update update = new Update().inc("age", increment)
.set("updatedAt", LocalDateTime.now());
return mongoTemplate.updateFirst(query, update, User.class);
}
/**
* 原子操作:乘除运算
*/
public UpdateResult multiplyUserScore(String userId, double multiplier) {
Query query = Query.query(Criteria.where("id").is(userId));
Update update = new Update().multiply("score", multiplier)
.set("updatedAt", LocalDateTime.now());
return mongoTemplate.updateFirst(query, update, User.class);
}
/**
* 数组操作:向数组添加元素
*/
public UpdateResult addRoleToUser(String userId, String role) {
Query query = Query.query(Criteria.where("id").is(userId));
Update update = new Update().addToSet("roles", role)
.set("updatedAt", LocalDateTime.now());
return mongoTemplate.updateFirst(query, update, User.class);
}
/**
* 数组操作:从数组移除元素
*/
public UpdateResult removeRoleFromUser(String userId, String role) {
Query query = Query.query(Criteria.where("id").is(userId));
Update update = new Update().pull("roles", role)
.set("updatedAt", LocalDateTime.now());
return mongoTemplate.updateFirst(query, update, User.class);
}
/**
* 数组操作:向数组添加多个元素
*/
public UpdateResult addRolesToUser(String userId, List<String> roles) {
Query query = Query.query(Criteria.where("id").is(userId));
Update update = new Update().pushAll("roles", roles.toArray())
.set("updatedAt", LocalDateTime.now());
return mongoTemplate.updateFirst(query, update, User.class);
}
/**
* 数组操作:更新数组中的特定元素
*/
public UpdateResult updateArrayElement(String userId, String oldRole, String newRole) {
Query query = Query.query(Criteria.where("id").is(userId)
.and("roles").is(oldRole));
Update update = Update.update("roles.$", newRole)
.set("updatedAt", LocalDateTime.now());
return mongoTemplate.updateFirst(query, update, User.class);
}
/**
* 使用FindAndModify原子操作
*/
public User findAndUpdateUser(String userId, Update update) {
Query query = Query.query(Criteria.where("id").is(userId));
return mongoTemplate.findAndModify(
query,
update,
FindAndModifyOptions.options().returnNew(true),
User.class
);
}
/**
* 批量更新操作
*/
public BulkWriteResult bulkUpdateUsers(List<User> users) {
BulkOperations bulkOps = mongoTemplate.bulkOps(
BulkOperations.BulkMode.ORDERED, User.class);
for (User user : users) {
Query query = Query.query(Criteria.where("id").is(user.getId()));
Update update = Update.update("email", user.getEmail())
.set("updatedAt", LocalDateTime.now());
bulkOps.updateOne(query, update);
}
return bulkOps.execute();
}
}3.4 删除操作
@Service
@Slf4j
@Transactional
public class UserDeleteService {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 根据ID删除
*/
public DeleteResult deleteById(String id) {
Query query = Query.query(Criteria.where("id").is(id));
return mongoTemplate.remove(query, User.class);
}
/**
* 根据条件删除
*/
public DeleteResult deleteByUsername(String username) {
Query query = Query.query(Criteria.where("username").is(username));
return mongoTemplate.remove(query, User.class);
}
/**
* 删除所有文档
*/
public DeleteResult deleteAllUsers() {
return mongoTemplate.remove(new Query(), User.class);
}
/**
* 删除集合(慎用!)
*/
public void dropCollection() {
mongoTemplate.dropCollection(User.class);
}
/**
* 软删除
*/
public UpdateResult softDeleteUser(String userId) {
Query query = Query.query(Criteria.where("id").is(userId));
Update update = Update.update("isDeleted", true)
.set("updatedAt", LocalDateTime.now());
return mongoTemplate.updateFirst(query, update, User.class);
}
/**
* 批量删除
*/
public DeleteResult deleteUsersByCity(String city) {
Query query = Query.query(Criteria.where("address.city").is(city));
return mongoTemplate.remove(query, User.class);
}
/**
* 使用FindAndRemove
*/
public User findAndRemove(String username) {
Query query = Query.query(Criteria.where("username").is(username));
return mongoTemplate.findAndRemove(query, User.class);
}
/**
* 删除所有软删除的文档
*/
public DeleteResult deleteAllSoftDeleted() {
Query query = Query.query(Criteria.where("isDeleted").is(true));
return mongoTemplate.remove(query, User.class);
}
}四、高级查询与聚合操作
4.1 复杂条件查询
@Service
@Slf4j
public class AdvancedQueryService {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 组合查询条件
*/
public List<User> findUsersWithComplexCriteria(String city, Integer minAge, Integer maxAge) {
Criteria criteria = new Criteria();
// 城市条件
if (StringUtils.isNotBlank(city)) {
criteria.and("address.city").is(city);
}
// 年龄范围条件
if (minAge != null && maxAge != null) {
criteria.and("age").gte(minAge).lte(maxAge);
} else if (minAge != null) {
criteria.and("age").gte(minAge);
} else if (maxAge != null) {
criteria.and("age").lte(maxAge);
}
// 邮箱不为空
criteria.and("email").ne(null);
Query query = Query.query(criteria);
return mongoTemplate.find(query, User.class);
}
/**
* OR条件查询
*/
public List<User> findUsersByCityOrAge(String city, Integer age) {
Criteria criteria = new Criteria().orOperator(
Criteria.where("address.city").is(city),
Criteria.where("age").is(age)
);
Query query = Query.query(criteria);
return mongoTemplate.find(query, User.class);
}
/**
* AND和OR组合查询
*/
public List<User> findUsersWithAndOr() {
Criteria criteria = new Criteria().andOperator(
Criteria.where("age").gte(18).lte(60),
new Criteria().orOperator(
Criteria.where("address.city").is("Beijing"),
Criteria.where("address.city").is("Shanghai")
)
);
Query query = Query.query(criteria);
return mongoTemplate.find(query, User.class);
}
/**
* 正则表达式查询
*/
public List<User> findUsersByEmailPattern(String pattern) {
Query query = Query.query(
Criteria.where("email").regex(pattern, "i") // i表示忽略大小写
);
return mongoTemplate.find(query, User.class);
}
/**
* 数组查询:包含所有元素
*/
public List<User> findUsersWithAllRoles(List<String> requiredRoles) {
Query query = Query.query(
Criteria.where("roles").all(requiredRoles)
);
return mongoTemplate.find(query, User.class);
}
/**
* 数组查询:包含任意元素
*/
public List<User> findUsersWithAnyRole(List<String> roles) {
Query query = Query.query(
Criteria.where("roles").in(roles)
);
return mongoTemplate.find(query, User.class);
}
/**
* 数组查询:数组大小
*/
public List<User> findUsersWithRoleCount(int minCount) {
Query query = Query.query(
Criteria.where("roles").size(minCount)
);
return mongoTemplate.find(query, User.class);
}
/**
* 日期范围查询
*/
public List<User> findUsersCreatedBetween(LocalDateTime start, LocalDateTime end) {
Query query = Query.query(
Criteria.where("createdAt").gte(start).lte(end)
);
return mongoTemplate.find(query, User.class);
}
/**
* 嵌套文档查询
*/
public List<User> findUsersByAddressDetail(String city, String street) {
Query query = Query.query(
Criteria.where("address.city").is(city)
.and("address.street").regex(street, "i")
);
return mongoTemplate.find(query, User.class);
}
/**
* 使用Text Search(需要创建文本索引)
*/
public List<User> findUsersByText(String searchText) {
TextCriteria criteria = TextCriteria.forDefaultLanguage()
.matching(searchText);
Query query = TextQuery.queryText(criteria)
.sortByScore() // 按相关性排序
.with(PageRequest.of(0, 10));
return mongoTemplate.find(query, User.class);
}
/**
* 地理空间查询:多边形范围内
*/
public List<User> findUsersInPolygon(List<double[]> polygonPoints) {
// polygonPoints格式:[[lon1, lat1], [lon2, lat2], ...]
Query query = Query.query(
Criteria.where("address.location")
.within(new GeoJsonPolygon(polygonPoints))
);
return mongoTemplate.find(query, User.class);
}
}4.2 聚合操作
@Service
@Slf4j
public class AggregationService {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 基础聚合:分组统计
*/
public List<Document> groupUsersByCity() {
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.group("address.city")
.count().as("userCount")
.avg("age").as("avgAge")
.sum("age").as("totalAge"),
Aggregation.project("userCount", "avgAge", "totalAge")
.and("_id").as("city"),
Aggregation.sort(Sort.Direction.DESC, "userCount"),
Aggregation.limit(10)
);
AggregationResults<Document> results = mongoTemplate.aggregate(
aggregation, "users", Document.class);
return results.getMappedResults();
}
/**
* 多阶段聚合:匹配、分组、排序
*/
public List<Document> aggregateActiveUsers() {
Aggregation aggregation = Aggregation.newAggregation(
// 阶段1:过滤条件
Aggregation.match(
Criteria.where("isDeleted").is(false)
.and("age").gte(18)
),
// 阶段2:按城市分组
Aggregation.group("address.city")
.count().as("activeUserCount")
.avg("age").as("averageAge"),
// 阶段3:投影
Aggregation.project()
.and("_id").as("city")
.and("activeUserCount").as("userCount")
.and("averageAge").as("avgAge")
.andExclude("_id"),
// 阶段4:排序
Aggregation.sort(Sort.Direction.DESC, "userCount"),
// 阶段5:分页
Aggregation.skip(0L),
Aggregation.limit(20)
);
return mongoTemplate.aggregate(aggregation, "users", Document.class)
.getMappedResults();
}
/**
* 聚合操作:展开数组
*/
public List<Document> unwindUserRoles() {
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(Criteria.where("roles").exists(true)),
Aggregation.unwind("roles"),
Aggregation.group("roles")
.count().as("userCount"),
Aggregation.sort(Sort.Direction.DESC, "userCount")
);
return mongoTemplate.aggregate(aggregation, "users", Document.class)
.getMappedResults();
}
/**
* 聚合操作:关联查询
*/
public List<Document> aggregateWithLookup() {
LookupOperation lookup = LookupOperation.newLookup()
.from("orders") // 关联订单集合
.localField("id")
.foreignField("userId")
.as("userOrders");
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(Criteria.where("address.city").is("Beijing")),
lookup,
Aggregation.unwind("userOrders", true), // 保留没有订单的用户
Aggregation.group("id")
.first("username").as("username")
.sum("userOrders.amount").as("totalSpent")
.count().as("orderCount"),
Aggregation.match(Criteria.where("orderCount").gt(0)),
Aggregation.sort(Sort.Direction.DESC, "totalSpent")
);
return mongoTemplate.aggregate(aggregation, "users", Document.class)
.getMappedResults();
}
/**
* 聚合操作:添加计算字段
*/
public List<Document> addComputedField() {
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.project()
.and("username").as("username")
.and("age").as("age")
.andExpression("year(createdAt)").as("creationYear")
.andExpression("concat(address.city, ', ', address.country)").as("fullAddress")
.andExpression("CASE WHEN age >= 18 THEN 'Adult' ELSE 'Minor' END").as("ageGroup"),
Aggregation.sort(Sort.Direction.ASC, "creationYear")
);
return mongoTemplate.aggregate(aggregation, "users", Document.class)
.getMappedResults();
}
/**
* 聚合操作:分桶
*/
public List<Document> bucketUsersByAge() {
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.bucket("age")
.withBoundaries(0, 18, 30, 50, 100)
.withDefaultBucket("Unknown")
.andOutputCount().as("count")
.andOutput("username").push().as("users")
);
return mongoTemplate.aggregate(aggregation, "users", Document.class)
.getMappedResults();
}
/**
* 聚合操作:抽样
*/
public List<User> sampleUsers(int sampleSize) {
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.sample(sampleSize)
);
return mongoTemplate.aggregate(aggregation, "users", User.class)
.getMappedResults();
}
/**
* 聚合操作:图形查找
*/
public List<Document> graphLookup() {
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(Criteria.where("username").is("admin")),
Aggregation.graphLookup("users")
.startWith("$reportsTo")
.connectFrom("reportsTo")
.connectTo("id")
.as("managersChain")
);
return mongoTemplate.aggregate(aggregation, "users", Document.class)
.getMappedResults();
}
}4.3 事务管理
@Service
@Slf4j
@Transactional
public class TransactionalService {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 声明式事务
*/
@Transactional(rollbackFor = Exception.class)
public void transferPoints(String fromUserId, String toUserId, int points) {
// 减少转出用户的积分
Query query1 = Query.query(Criteria.where("id").is(fromUserId));
Update update1 = Update.update("points", points).inc("points", -points);
UpdateResult result1 = mongoTemplate.updateFirst(query1, update1, User.class);
if (result1.getModifiedCount() == 0) {
throw new RuntimeException("转出用户不存在或积分不足");
}
// 增加转入用户的积分
Query query2 = Query.query(Criteria.where("id").is(toUserId));
Update update2 = Update.update("points", points).inc("points", points);
UpdateResult result2 = mongoTemplate.updateFirst(query2, update2, User.class);
if (result2.getModifiedCount() == 0) {
throw new RuntimeException("转入用户不存在");
}
// 记录交易日志
TransactionLog log = TransactionLog.builder()
.fromUserId(fromUserId)
.toUserId(toUserId)
.points(points)
.timestamp(LocalDateTime.now())
.build();
mongoTemplate.insert(log);
}
/**
* 编程式事务
*/
public void transferPointsWithProgrammaticTransaction(String fromUserId, String toUserId, int points) {
TransactionTemplate transactionTemplate = new TransactionTemplate(
new MongoTransactionManager(mongoTemplate.getMongoDatabaseFactory())
);
transactionTemplate.execute(status -> {
try {
// 执行事务操作
Query query1 = Query.query(Criteria.where("id").is(fromUserId));
Update update1 = Update.update("points", points).inc("points", -points);
mongoTemplate.updateFirst(query1, update1, User.class);
Query query2 = Query.query(Criteria.where("id").is(toUserId));
Update update2 = Update.update("points", points).inc("points", points);
mongoTemplate.updateFirst(query2, update2, User.class);
// 记录日志
TransactionLog log = TransactionLog.builder()
.fromUserId(fromUserId)
.toUserId(toUserId)
.points(points)
.timestamp(LocalDateTime.now())
.build();
mongoTemplate.insert(log);
return true;
} catch (Exception e) {
status.setRollbackOnly();
throw new RuntimeException("转账失败", e);
}
});
}
/**
* 多文档事务
*/
@Transactional
public void multiDocumentTransaction() {
// 操作1:更新用户
User user = new User();
user.setId("user1");
user.setUsername("updated");
mongoTemplate.save(user);
// 操作2:插入日志
AuditLog auditLog = new AuditLog();
auditLog.setAction("UPDATE_USER");
auditLog.setUserId("user1");
mongoTemplate.insert(auditLog);
// 操作3:更新计数器
Query query = Query.query(Criteria.where("name").is("user_update_counter"));
Update update = Update.update("count", 1).inc("count", 1);
mongoTemplate.upsert(query, update, Counter.class);
// 如果发生异常,所有操作都会回滚
}
}五、GridFS操作
@Service
@Slf4j
public class GridFsService {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 存储文件到GridFS
*/
public String storeFile(String filename, InputStream inputStream, String contentType) {
GridFsTemplate gridFsTemplate = new GridFsTemplate(
mongoTemplate.getMongoDatabaseFactory(),
mongoTemplate.getConverter()
);
// 设置元数据
ObjectMetadata metadata = new ObjectMetadata();
metadata.put("uploadedBy", "system");
metadata.put("uploadTime", LocalDateTime.now().toString());
// 存储文件
GridFSFile file = gridFsTemplate.store(
inputStream,
filename,
contentType,
metadata
);
return file.getObjectId().toString();
}
/**
* 从GridFS读取文件
*/
public GridFsResource getFile(String fileId) {
GridFsTemplate gridFsTemplate = new GridFsTemplate(
mongoTemplate.getMongoDatabaseFactory(),
mongoTemplate.getConverter()
);
GridFSFile file = gridFsTemplate.findOne(
Query.query(Criteria.where("_id").is(new ObjectId(fileId)))
);
if (file == null) {
throw new RuntimeException("File not found");
}
return gridFsTemplate.getResource(file);
}
/**
* 删除GridFS文件
*/
public void deleteFile(String fileId) {
GridFsTemplate gridFsTemplate = new GridFsTemplate(
mongoTemplate.getMongoDatabaseFactory(),
mongoTemplate.getConverter()
);
gridFsTemplate.delete(Query.query(Criteria.where("_id").is(new ObjectId(fileId))));
}
/**
* 查询GridFS文件
*/
public List<GridFSFile> findFilesByMetadata(String key, String value) {
GridFsTemplate gridFsTemplate = new GridFsTemplate(
mongoTemplate.getMongoDatabaseFactory(),
mongoTemplate.getConverter()
);
return gridFsTemplate.find(
Query.query(Criteria.where("metadata." + key).is(value))
);
}
}六、性能优化与监控
6.1 索引管理
@Service
@Slf4j
public class IndexManagementService {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 创建索引
*/
public void createIndexes() {
IndexOperations indexOps = mongoTemplate.indexOps(User.class);
// 创建单字段索引
indexOps.ensureIndex(new Index().on("username", Sort.Direction.ASC));
// 创建唯一索引
indexOps.ensureIndex(new Index().on("email", Sort.Direction.ASC).unique());
// 创建复合索引
indexOps.ensureIndex(
new Index().on("address.city", Sort.Direction.ASC)
.on("age", Sort.Direction.DESC)
.named("city_age_idx")
);
// 创建TTL索引(自动过期)
indexOps.ensureIndex(
new Index().on("createdAt", Sort.Direction.ASC)
.expire(30, TimeUnit.DAYS) // 30天后自动删除
);
// 创建文本索引
indexOps.ensureIndex(
new Index().on("username", Sort.Direction.ASC)
.on("email", Sort.Direction.ASC)
.named("text_search_idx")
);
// 创建地理空间索引
indexOps.ensureIndex(
new Index().on("address.location", IndexDirection.GEO_2DSPHERE)
);
// 创建部分索引
indexOps.ensureIndex(
new Index().on("age", Sort.Direction.ASC)
.partial(Filter.filter(Criteria.where("age").gte(18)))
);
}
/**
* 获取所有索引
*/
public List<Document> getAllIndexes() {
IndexOperations indexOps = mongoTemplate.indexOps(User.class);
return indexOps.getIndexInfo();
}
/**
* 删除索引
*/
public void dropIndex(String indexName) {
IndexOperations indexOps = mongoTemplate.indexOps(User.class);
indexOps.dropIndex(indexName);
}
/**
* 重建索引
*/
public void rebuildIndexes() {
IndexOperations indexOps = mongoTemplate.indexOps(User.class);
indexOps.dropAllIndexes();
createIndexes();
}
/**
* 检查索引是否存在
*/
public boolean indexExists(String indexName) {
IndexOperations indexOps = mongoTemplate.indexOps(User.class);
return indexOps.getIndexInfo().stream()
.anyMatch(index -> index.getName().equals(indexName));
}
}6.2 性能监控
@Aspect
@Component
@Slf4j
public class MongoPerformanceMonitor {
private final MeterRegistry meterRegistry;
public MongoPerformanceMonitor(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
@Around("execution(* org.springframework.data.mongodb.core.MongoTemplate.*(..))")
public Object monitorMongoOperations(ProceedingJoinPoint joinPoint) throws Throwable {
String operationName = joinPoint.getSignature().getName();
long startTime = System.currentTimeMillis();
try {
Object result = joinPoint.proceed();
long duration = System.currentTimeMillis() - startTime;
// 记录指标
meterRegistry.timer("mongodb.operations", "operation", operationName)
.record(duration, TimeUnit.MILLISECONDS);
// 记录慢查询
if (duration > 1000) { // 超过1秒
log.warn("Slow MongoDB operation detected: {} took {}ms",
operationName, duration);
if (log.isDebugEnabled()) {
Object[] args = joinPoint.getArgs();
log.debug("Operation arguments: {}", Arrays.toString(args));
}
}
return result;
} catch (Exception e) {
// 记录错误指标
meterRegistry.counter("mongodb.errors", "operation", operationName)
.increment();
throw e;
}
}
}
/**
* 健康检查配置
*/
@Component
public class MongoHealthIndicator implements HealthIndicator {
@Autowired
private MongoTemplate mongoTemplate;
@Override
public Health health() {
try {
// 执行简单查询检查连接
Document stats = mongoTemplate.executeCommand("{ serverStatus: 1 }");
return Health.up()
.withDetail("version", stats.get("version"))
.withDetail("host", mongoTemplate.getDb().getName())
.withDetail("collections", mongoTemplate.getCollectionNames())
.build();
} catch (Exception e) {
return Health.down()
.withException(e)
.build();
}
}
}6.3 查询优化
@Service
@Slf4j
public class QueryOptimizationService {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 使用投影优化查询
*/
public List<User> optimizedFindUsers() {
Query query = new Query();
// 只返回需要的字段
query.fields()
.include("username")
.include("email")
.exclude("_id");
// 限制返回数量
query.limit(100);
// 使用索引覆盖查询
query.withHint("username_1_email_1");
return mongoTemplate.find(query, User.class);
}
/**
* 批量操作优化
*/
public void batchOptimizedOperations() {
// 使用BulkOperations进行批量操作
BulkOperations bulkOps = mongoTemplate.bulkOps(
BulkOperations.BulkMode.UNORDERED, User.class);
// 批量插入
List<User> usersToInsert = generateUsers(1000);
bulkOps.insert(usersToInsert);
// 批量更新
Query updateQuery = Query.query(Criteria.where("age").lt(18));
Update update = Update.update("category", "minor");
bulkOps.updateMulti(updateQuery, update);
// 执行批量操作
BulkWriteResult result = bulkOps.execute();
log.info("Bulk operation result: {}", result);
}
/**
* 使用游标处理大数据集
*/
public void processLargeDatasetWithCursor() {
Query query = Query.query(Criteria.where("createdAt")
.gte(LocalDateTime.now().minusMonths(1)));
// 设置游标选项
query.cursorBatchSize(1000); // 每批获取1000条
try (MongoCursor<User> cursor = mongoTemplate.stream(query, User.class)) {
int count = 0;
while (cursor.hasNext()) {
User user = cursor.next();
processUser(user);
count++;
// 每处理1000条记录一次日志
if (count % 1000 == 0) {
log.info("Processed {} records", count);
}
}
}
}
/**
* 使用聚合管道优化复杂查询
*/
public List<Document> optimizedAggregation() {
Aggregation aggregation = Aggregation.newAggregation(
// 尽早过滤数据
Aggregation.match(Criteria.where("isActive").is(true)),
// 使用索引字段进行排序
Aggregation.sort(Sort.Direction.DESC, "createdAt"),
// 限制数据量
Aggregation.limit(1000),
// 只投影需要的字段
Aggregation.project()
.and("username").as("name")
.and("email").as("contact")
.andExclude("_id"),
// 最后进行复杂计算
Aggregation.addFields()
.addField("nameLength").withValueOf(StringOperators.strLenCP("$name"))
.build()
);
return mongoTemplate.aggregate(aggregation, "users", Document.class)
.getMappedResults();
}
private List<User> generateUsers(int count) {
List<User> users = new ArrayList<>();
for (int i = 0; i < count; i++) {
User user = new User();
user.setUsername("user" + i);
user.setEmail("user" + i + "@example.com");
user.setAge(new Random().nextInt(50) + 18);
users.add(user);
}
return users;
}
private void processUser(User user) {
// 处理逻辑
}
}七、最佳实践总结
7.1 设计原则
- 合理使用索引:为查询频繁的字段创建索引,注意索引顺序
- 避免大文档:单个文档不应超过16MB,考虑拆分大文档
- 选择合适的数据模型:根据查询模式选择内嵌或引用
- 使用合适的字段类型:选择最合适的BSON类型存储数据
7.2 性能优化
- 查询优化:
- 使用投影减少返回字段
- 合理使用分页和限制
- 避免全表扫描
- 写入优化:
- 使用批量操作
- 合理使用索引(避免过多索引影响写入性能)
- 考虑写入确认级别
- 连接池优化:
- 根据并发量调整连接池大小
- 监控连接使用情况
- 合理设置超时时间
7.3 监控告警
- 健康检查:定期检查MongoDB连接状态
- 性能监控:监控查询响应时间、错误率
- 资源监控:监控CPU、内存、磁盘使用情况
- 慢查询日志:记录和分析慢查询
7.4 安全实践
- 认证授权:使用用户名密码认证
- 网络隔离:在内网环境中运行
- 加密传输:启用TLS/SSL加密
- 定期备份:制定备份和恢复策略
八、完整示例项目
8.1 项目结构
src/main/java/com/example/mongodemo/ ├── config/ │ ├── MongoConfig.java │ ├── MongoAuditConfig.java │ └── MultiMongoConfig.java ├── entity/ │ ├── BaseEntity.java │ ├── User.java │ ├── Address.java │ └── AuditLog.java ├── repository/ │ ├── UserRepository.java │ └── CustomUserRepository.java ├── service/ │ ├── UserService.java │ ├── UserQueryService.java │ ├── AggregationService.java │ └── TransactionService.java ├── controller/ │ └── UserController.java └── MongodemoApplication.java
8.2 启动类配置
@SpringBootApplication
@EnableMongoRepositories
@EnableTransactionManagement
@EnableCaching
@Slf4j
public class MongodemoApplication {
public static void main(String[] args) {
SpringApplication.run(MongodemoApplication.class, args);
}
@Bean
public CommandLineRunner initData(UserService userService) {
return args -> {
log.info("Initializing sample data...");
// 创建测试用户
User user = User.builder()
.username("testuser")
.email("test@example.com")
.age(25)
.address(Address.builder()
.city("Beijing")
.country("China")
.build())
.build();
userService.insertUser(user);
log.info("Sample user created: {}", user.getUsername());
};
}
}
九、总结
通过本文的全面介绍,您应该已经掌握了Spring Boot中MongoTemplate的核心使用方法。关键点包括:
- 配置灵活:支持单机、副本集、分片集群等多种部署方式
- 操作全面:涵盖CRUD、聚合、事务、GridFS等所有操作
- 性能优秀:提供丰富的性能优化选项和监控手段
- 易于集成:与Spring生态系统无缝集成
在实际项目中,建议根据具体业务需求选择合适的操作方式,并持续监控和优化数据库性能。
到此这篇关于Spring Boot中MongoTemplate深度指南:从入门到实战的文章就介绍到这了,更多相关springboot mongotemplate使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
