db-mongo 是 EasyFK 框架中面向 MongoDB 文档数据库的数据访问组件。该模块基于 Spring Data MongoDB,采用 Entity → DAO → Repository 分层架构,提供智能 ID 管理、自动审计、泛型 CRUD、灵活条件查询构建以及 DTO/实体自动转换能力,适用于内容管理、用户画像、日志存储、电商商品等文档型数据场景。
<dependency>
<groupId>com.mcst</groupId>
<artifactId>db-mongo</artifactId>
</dependency>dependencies {
implementation 'com.mcst:db-mongo'
}> 版本号由框架统一 BOM 管理,无需手动指定。
该模块会自动传递引入以下依赖:
在 application.yml 中配置 MongoDB 连接信息:
spring:
data:
mongodb:
uri: mongodb://localhost:27017/easyfk或分别配置各项参数:
spring:
data:
mongodb:
host: localhost
port: 27017
database: easyfk
username: your_username
password: your_password通过 URI 参数配置连接池:
spring:
data:
mongodb:
uri: mongodb://localhost:27017/easyfk?maxPoolSize=50&minPoolSize=10&maxIdleTimeMS=60000模块通过 MongoAutoConfig 自动完成以下配置:
| MongoDB 审计 | 自动启用 `@EnableMongoAuditing`,支持 `@CreatedDate` 和 `@LastModifiedDate` |
|---|---|
| 激活条件 | 当 classpath 中存在 `MongoTemplate` 时自动激活 |
模块采用三层架构,职责清晰:
┌────────────────────────────────────────────────┐
│ Service 层 │
│ 注入 Repository,调用业务方法 │
└──────────────────────┬─────────────────────────┘
│
┌──────────────────────▼─────────────────────────┐
│ Repository 层 (BaseMongoRepositoryImpl) │
│ DTO ↔ Entity 自动转换 · 实现 IBaseRepository │
└──────────────────────┬─────────────────────────┘
│
┌──────────────────────▼─────────────────────────┐
│ DAO 层 (BaseSpringMongoDAO) │
│ CRUD 操作 · MongoTemplate 封装 │
└──────────────────────┬─────────────────────────┘
│
┌──────────────────────▼─────────────────────────┐
│ Entity 层 (BaseMongoEntity) │
│ 智能 ID · 自动审计 · 序列化支持 │
└────────────────────────────────────────────────┘所有 MongoDB 实体类需继承 BaseMongoEntity,自动获得以下能力:
| `objectId` | `ObjectId` | `@Id` | MongoDB 主键(`_id`),持久化存储 |
|---|---|---|---|
| `createdAt` | `LocalDateTime` | `@CreatedDate` | 创建时间,插入时自动设置 |
| `updatedAt` | `LocalDateTime` | `@LastModifiedDate` | 更新时间,插入和更新时自动设置 |
智能 ID 转换机制:
@Data
@EqualsAndHashCode(callSuper = true)
@Document(collection = "products")
public class Product extends BaseMongoEntity {
@Indexed(unique = true)
private String productCode;
private String name;
private String description;
private BigDecimal price;
private Integer stock;
private String category;
private String status;
}> 无需声明 id、objectId、createdAt、updatedAt 字段,均从基类继承。建议使用 @Document 指定集合名,@Indexed 标记索引字段。
继承 BaseSpringMongoDAO<T>,即可获得全部 CRUD 能力:
@Repository
public class ProductDAO extends BaseSpringMongoDAO<Product> {
// 可在此添加自定义查询方法
}| `insert(entity)` | 实体对象 | void | 插入单条记录 |
|---|---|---|---|
| `insertAndReturnId(entity)` | 实体对象 | `String` | 插入并返回字符串形式 ID |
| `saveOrUpdate(entity)` | 实体对象 | void | 存在则更新,不存在则插入 |
| `deleteById(id)` | 主键 | `DeleteResult` | 按 ID 删除 |
|---|---|---|---|
| `deleteByQuery(query)` | `Query` | `DeleteResult` | 按 Query 对象删除 |
| `delete(entity)` | 实体对象 | `DeleteResult` | 按实体删除 |
| `findById(id, selectColumns...)` | 主键, 可选字段 | `T` | 按 ID 查询(支持字段选择) |
|---|---|---|---|
| `findOne(valueMap)` | `Map<String, Object>` | `T` | 按多字段匹配查询一条 |
| `findOne(query)` | `Query` | `T` | 按 Query 对象查询一条 |
| `findOneByEntity(example)` | 示例实体 | `T` | 按示例实体查询一条(非空字段匹配) |
| `findByField(field, value)` | 字段名, 值 | `List<T>` | 按单字段查询列表 |
| `findByField(valueMap)` | `Map<String, Object>` | `List<T>` | 按多字段匹配查询列表 |
| `findByEntity(example)` | 示例实体 | `List<T>` | 按示例实体查询列表 |
| `findByCondition(condition)` | `SearchCondition` | `List<T>` | 按条件查询列表 |
| `findOneByCondition(condition)` | `SearchCondition` | `T` | 按条件查询单条 |
| `findByPage(condition)` | `SearchCondition` | `PageResult<T>` | 分页查询(含总数) |
| `findAll()` | — | `List<T>` | 查询全部记录 |
| `updateEntity(entity)` | 实体对象 | `UpdateResult` | **全量更新**:null 字段会被 unset |
|---|---|---|---|
| `updateEntityByCondition(entity, condition)` | 实体对象, 查询条件 | `UpdateResult` | **条件批量更新**:批量写入非空字段 |
| `updateFirst(entity, condition)` | 实体对象, 查询条件 | `UpdateResult` | 条件更新第一条匹配记录 |
| `atomicIncrement(condition, field, value)` | 查询条件, 字段名, 增量值 | `T` | 原子递增字段值,返回更新后实体 |
> 更新语义说明:
> - updateEntity:全量覆盖,未赋值字段会被 unset,适合完全同步实体的场景
> - updateEntitySelective:增量更新,只 set 非空字段;若需要清空字段,在 nullProperties 中显式声明
> - updateEntityByCondition:条件增量更新,批量写入非空字段,自动刷新 updatedAt
> - 所有更新操作会自动设置 updatedAt 为当前时间
| `count()` | — | `long` | 统计全部记录数 |
|---|---|---|---|
| `count(query)` | `Query` | `long` | 按 Query 统计 |
| `count(condition)` | `SearchCondition` | `long` | 按条件统计 |
| `countByEntity(entity)` | 示例实体 | `long` | 按示例实体统计 |
| `countByCondition(condition)` | `SearchCondition` | `long` | 按条件统计 |
| `exists(field, value)` | 字段名, 值 | `boolean` | 判断字段值是否存在 |
| `exists(condition)` | `SearchCondition` | `boolean` | 按条件判断是否存在 |
| `exists(query)` | `Query` | `boolean` | 按 Query 判断是否存在 |
| `exists(example)` | 示例实体 | `boolean` | 按示例实体判断是否存在 |
BaseMongoRepositoryImpl 负责 DTO 与 Entity 之间的自动转换,面向 Service 层提供统一接口:
@Repository
public class ProductRepository
extends BaseMongoRepositoryImpl<ProductDAO, ProductDTO, Product, String> {
// 可在此添加自定义业务查询方法
}泛型参数说明:
| `D` | DAO 类型 | `ProductDAO` |
|---|---|---|
| `P` | Entity 类型(数据层使用) | `Product` |
| `PK` | 主键类型 | `String` |
| `queryById(id, selectColumns...)` | 主键, 可选字段 | `T` (DTO) | 按 ID 查询 |
|---|---|---|---|
| `queryOneByCondition(condition)` | `SearchCondition` | `T` | 条件查询单条 |
| `queryOneByField(field, value, selectFields...)` | 字段名, 值, 可选字段 | `T` | 按字段查询单条 |
| `queryByField(field, value, selectFields...)` | 字段名, 值, 可选字段 | `List<T>` | 按字段查询列表 |
| `queryByPage(condition)` | `SearchCondition` | `PageResult<T>` | 分页查询 |
| `insert(param)` | DTO 对象 | `BaseResult<?>` | 插入 |
| `insertAndReturnId(param)` | DTO 对象 | `PK` | 插入并返回 ID |
| `insertBatch(data)` | DTO 列表 | `BaseResult<?>` | 批量插入 |
| `deleteById(id)` | 主键 | `BaseResult<?>` | 按 ID 删除 |
| `deleteByCondition(condition)` | `SearchCondition` | `BaseResult<?>` | 条件删除 |
| `updateBySelective(param, nullProps...)` | DTO 对象, 需清空字段名 | `BaseResult<?>` | 增量更新 |
| `updateEntityByCondition(param, condition)` | DTO 对象, 查询条件 | `BaseResult<?>` | 条件批量更新 |
| `saveOrUpdateBySelective(param, nullProps...)` | DTO 对象, 需清空字段名 | `BaseResult<?>` | 有 ID 则更新,无 ID 则插入 |
| `exists(field, value)` | 字段名, 值 | `boolean` | 存在性检查 |
| `exists(condition)` | `SearchCondition` | `boolean` | 条件存在性检查 |
| `countByCondition(condition)` | `SearchCondition` | `long` | 条件统计 |
通过 SCBuilder 构建 SearchCondition,MongoConditionBuilder 会自动转换为 MongoDB Query:
SearchCondition condition = SCBuilder.builder()
.equalsConditions("category", "electronics")
.likeConditions("name", ".*iPhone.*")
.rangeConditions(new RangeCondition("price", 500.0, null, RangeConditionType.GreaterThan))
.descFields("createdAt")
.pageSearch(new PageSearch(1, 20))
.build();| 精确匹配 | `equalsConditions(key, value)` | 字段等于指定值 |
|---|---|---|
| 模糊匹配 | `likeConditions(key, pattern)` | 正则表达式匹配 |
| IN 查询 | `inConditions(key, values)` | 字段值在列表中 |
| 范围查询 | `rangeConditions(RangeCondition...)` | 大于/大于等于/小于/小于等于/Between |
| 升序排序 | `ascFields(fields...)` | 升序排序字段 |
| 降序排序 | `descFields(fields...)` | 降序排序字段 |
| 分页 | `pageSearch(PageSearch)` | 分页参数 |
| 字段选择 | `selectFields(fields...)` | 只查询指定字段 |
| 限制数量 | `top(count)` | 限制返回记录数 |
| `GreaterThan` | 大于 (`>`) | `new RangeCondition("price", 100.0, null, RangeConditionType.GreaterThan)` |
|---|---|---|
| `LessThan` | 小于 (`<`) | `new RangeCondition("age", 18, null, RangeConditionType.LessThan)` |
| `LessThanOrEqual` | 小于等于 (`<=`) | `new RangeCondition("discount", 90, null, RangeConditionType.LessThanOrEqual)` |
| `Equal` | 等于 (`=`) | `new RangeCondition("level", 5, null, RangeConditionType.Equal)` |
| `Between` | 介于之间 (`>= AND <=`) | `new RangeCondition("price", 100.0, 1000.0, RangeConditionType.Between)` |
> Between 类型:只提供 startValue 等价于 >=,只提供 endValue 等价于 <=。
如果实体类包含 createdAt 字段且未指定排序条件,查询会自动按 createdAt 降序排列。
@Data
public class ProductDTO implements Serializable {
private String id;
private String productCode;
private String name;
private String description;
private BigDecimal price;
private Integer stock;
private String category;
private String status;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}ProductDTO dto = new ProductDTO();
dto.setProductCode("PROD-001");
dto.setName("iPhone 15");
dto.setPrice(new BigDecimal("7999.00"));
dto.setStock(100);
String id = productRepository.insertAndReturnId(dto);List<ProductDTO> products = new ArrayList<>();
for (int i = 0; i < 5000; i++) {
ProductDTO dto = new ProductDTO();
dto.setName("Product " + i);
dto.setPrice(BigDecimal.valueOf(100 + i));
products.add(dto);
}
// 自动分批,每批 1000 条
productRepository.insertBatch(products);SearchCondition condition = SCBuilder.builder()
.equalsConditions("category", "electronics")
.equalsConditions("status", "ACTIVE")
.rangeConditions(new RangeCondition("price", 100.0, 1000.0, RangeConditionType.Between))
.descFields("createdAt")
.build();
List<ProductDTO> products = productRepository.queryByCondition(condition);SearchCondition condition = SCBuilder.builder()
.equalsConditions("category", "electronics")
.descFields("createdAt")
.pageSearch(new PageSearch(1, 20))
.build();
PageResult<ProductDTO> page = productRepository.queryByPage(condition);
// page.getRows() — 当前页数据
// page.getTotal() — 总记录数SearchCondition condition = SCBuilder.builder()
.likeConditions("name", ".*iPhone.*")
.build();
List<ProductDTO> products = productRepository.queryByCondition(condition);// 方式一:Repository 层直接指定
ProductDTO product = productRepository.queryById(id, "name", "price", "stock");
// 方式二:通过 SearchCondition 指定
SearchCondition condition = SCBuilder.builder()
.equalsConditions("category", "electronics")
.selectFields("name", "price", "stock")
.build();
List<ProductDTO> products = productRepository.queryByCondition(condition);ProductDTO update = new ProductDTO();
update.setId(productId);
update.setPrice(new BigDecimal("6999.00"));
update.setStock(50);
// 仅更新 price 和 stock 字段
productRepository.updateBySelective(update);
// 更新 price 和 stock,同时清空 description 和 remark 字段
productRepository.updateBySelective(update, "description", "remark");ProductDTO template = new ProductDTO();
template.setStatus("INACTIVE");
SearchCondition condition = SCBuilder.builder()
.rangeConditions(new RangeCondition("stock", null, 0, RangeConditionType.LessThanOrEqual))
.build();
productRepository.updateEntityByCondition(template, condition);ProductDTO dto = new ProductDTO();
dto.setId(existingId); // 有 ID → 更新
dto.setName("Updated Name");
productRepository.saveOrUpdateBySelective(dto);
ProductDTO newDto = new ProductDTO(); // 无 ID → 插入
newDto.setName("New Product");
productRepository.saveOrUpdateBySelective(newDto);SearchCondition condition = SCBuilder.builder()
.equalsConditions("productCode", "PROD-001")
.build();
// stock 字段原子减 1,返回更新后的实体
Product updated = productDAO.atomicIncrement(condition, "stock", -1);Product example = new Product();
example.setCategory("electronics");
example.setStatus("ACTIVE");
// 查询所有 category=electronics 且 status=ACTIVE 的记录
List<Product> products = productDAO.findByEntity(example);
// 检查是否存在
boolean exists = productDAO.exists(example);// 按 ID 删除
productRepository.deleteById(productId);
// 按条件删除
SearchCondition condition = SCBuilder.builder()
.equalsConditions("status", "DISCONTINUED")
.build();
productRepository.deleteByCondition(condition);@Service
public class ProductService {
@Resource
private ProductRepository productRepository;
public BaseResult<?> createProduct(ProductDTO dto) {
return productRepository.insert(dto);
}
public PageResult<ProductDTO> searchProducts(String keyword, String category,
BigDecimal minPrice, BigDecimal maxPrice,
int page, int size) {
SCBuilder builder = SCBuilder.builder();
if (EmptyUtil.isNotEmpty(keyword)) {
builder.likeConditions("name", ".*" + keyword + ".*");
}
if (EmptyUtil.isNotEmpty(category)) {
builder.equalsConditions("category", category);
}
if (minPrice != null || maxPrice != null) {
builder.rangeConditions(
new RangeCondition("price", minPrice, maxPrice, RangeConditionType.Between));
}
SearchCondition condition = builder
.equalsConditions("status", "ACTIVE")
.descFields("createdAt")
.pageSearch(new PageSearch(page, size))
.build();
return productRepository.queryByPage(condition);
}
}com.mcst.easyfk.db.mongo
├── config
│ └── MongoAutoConfig.java # Spring Boot 自动配置类
├── dao
│ └── BaseSpringMongoDAO.java # 通用 DAO 基类
├── entities
│ └── BaseMongoEntity.java # 实体基类(ID 管理 + 审计字段)
├── repository
│ └── BaseMongoRepositoryImpl.java # 通用 Repository 实现(DTO/Entity 转换)
└── util
└── MongoConditionBuilder.java # 查询条件构建器(SearchCondition → Query)1. 实体类设计:继承 BaseMongoEntity 即可,无需声明 ID 和审计字段。使用 @Document 指定集合名,@Indexed 标记索引字段。
2. 分层使用:Service 层注入 Repository(操作 DTO),仅在需要底层操作时直接注入 DAO。
3. 增量更新优先:使用 updateEntitySelective / updateBySelective 而非 updateEntity,避免意外清空字段。
4. 字段选择查询:不需要完整对象时,使用 selectFields 减少网络传输和内存占用。
5. 批量操作:大量数据写入使用 insertBatch()(自动分批),批量修改使用 updateEntityByCondition()。
6. 分页查询:大数据集避免 findAll(),使用 findByPage() / queryByPage() 分页查询。
7. 索引优化:为常用查询字段添加 @Indexed,为复合查询使用 @CompoundIndex。
8. 模糊查询转义:likeConditions 使用正则表达式,注意转义特殊字符(如 .、*、()。
9. 事务支持:MongoDB 4.0+ 副本集环境下,可使用 Spring @Transactional 实现多文档事务。
easyfk-db-mongo — 文档型 NoSQL 数据库集成方案。