springboot程序查询mongo可以借助MongoRepository或者MongoTemplate,这里先介绍MongoRepository
一、entity
以下表格对比了 JPA 在 MySQL 和 MongoDB 中实体(Entity)上的相同点和不同点:
对比维度 | MySQL(基于 JPA 标准,结合 Hibernate 等实现) | MongoDB(基于 Spring Data MongoDB) |
---|---|---|
数据模型 | 关系型数据库,实体类映射到数据库表,表结构相对固定,需要预先定义表和字段,添加字段通常需修改表结构 | 文档型数据库,实体类映射到 MongoDB 的文档,文档结构灵活,字段可动态添加 |
主键注解 |
@Id 注解标识表的主键,可搭配 @GeneratedValue 等注解指定主键生成策略,如自增、UUID 等 |
@Id 注解标识文档的主键,对应 MongoDB 的 _id 字段,主键生成策略可自定义或依赖 MongoDB 自动生成 |
字段映射注解 |
@Column 注解用于将实体类字段映射到数据库表的列,可指定列名、数据类型、长度、是否可为空等属性 |
@Field 注解用于将实体类字段映射到 MongoDB 文档中的字段名,可指定文档中的字段名,对数据类型约束相对宽松 |
索引注解 | 使用 @Index 注解或在数据库层面创建索引,支持多种索引类型,如普通索引、唯一索引、复合索引等,通过 SQL 语句或注解指定索引相关属性 |
@Indexed 注解为字段创建索引,可创建单字段索引和复合索引(使用 @CompoundIndex 注解),还支持 TTL(Time - To - Live)索引用于自动过期文档 |
关联关系注解 | 支持多种关联关系,如 @OneToOne 、@OneToMany 、@ManyToOne 、@ManyToMany ,通过外键来维护表之间的关联关系 |
MongoDB 本身没有传统意义上的外键关联,但 Spring Data MongoDB 提供了一些方式模拟关联,如引用其他文档的 _id ,关联关系的维护和查询方式与 MySQL 不同 |
时间戳处理 | 通常需要手动编写代码或使用数据库触发器来处理记录的创建时间和修改时间,也可借助 Spring Data JPA 的 @CreatedDate 和 @LastModifiedDate 注解,但可能需额外配置 |
Spring Data MongoDB 可使用 @CreatedDate 和 @LastModifiedDate 注解自动记录文档的创建时间和最后修改时间,配置相对简单 |
乐观锁注解 |
@Version 注解用于实现乐观锁机制,通过版本号字段确保并发更新时数据的一致性 |
@Version 注解同样用于实现乐观锁机制,在 MongoDB 中作用与 MySQL 类似 |
非持久化字段注解 |
@Transient 注解标记的字段不会被持久化到数据库中,仅在内存中使用 |
@Transient 注解含义相同,标记的字段不会存储到 MongoDB 文档中 |
mongodb的entity常用注解
注解名称 | 常见用法 | 作用 |
---|---|---|
@Document |
@Document(collection = "collectionName") :collection 指定该实体类对应 MongoDB 集合的名称;@Document(collection = "#{@beanName.methodName()}") :通过 Spring EL 表达式动态获取集合名称 |
将 Java 类映射到 MongoDB 集合,可静态或动态指定集合名称 |
@Id |
@Id :直接标记实体类的主键字段 |
标记实体类的主键字段,在 JPA 中常与 @GeneratedValue 配合使用指定主键生成策略,在 MongoDB 中需手动处理主键生成 |
@MongoId |
@MongoId :默认使用 ObjectId 作为主键类型;@MongoId(FieldType.STRING) :FieldType 指定主键字段的类型,如 STRING 等 |
标记 MongoDB 文档的主键字段,可指定主键类型 |
@Field |
@Field("fieldNameInDB") :fieldNameInDB 指定该实体类字段对应到 MongoDB 文档中的字段名 |
将 Java 实体类的字段映射到 MongoDB 文档中的字段名 |
@Indexed |
@Indexed :在字段上创建默认的单字段索引;@Indexed(name = "indexName") :name 为索引指定一个名称;@Indexed(name = "IDX_TTL", expireAfterSeconds = 60) :expireAfterSeconds 指定 TTL 索引中文档在指定字段值之后多少秒过期 |
在单个字段上创建索引,可指定索引名称;结合 expireAfterSeconds 创建 TTL 索引 |
@CompoundIndex |
@CompoundIndex(name = "indexName", def = "{'field1':1, 'field2':1}") :name 为复合索引指定名称;def 定义索引字段及排序方向,1 表示升序,-1 表示降序 |
在多个字段上创建复合索引,可指定索引名称和字段排序方向 |
@Version |
@Version :标记实体类中的版本号字段 |
实现乐观锁机制,确保并发环境下数据的一致性。每次更新文档时,版本号自动递增 |
@TypeAlias |
@TypeAlias("aliasName") :aliasName 为 Java 类定义的类型别名 |
为 Java 类定义一个类型别名,存储对象时使用别名代替全限定名 |
@DocumentReference |
@DocumentReference(lazy = true) :lazy 为 true 表示懒加载关联文档;@DocumentReference(lookup = "{'_id':'?#{#target}'}") :lookup 指定查询关联文档的条件,?#{#target} 表示使用当前字段的值作为查询条件 |
建立文档之间的引用关系,可设置懒加载和查询关联文档的条件 |
@CreatedDate |
@CreatedDate :标记实体类中用于记录创建时间的字段 |
自动记录文档的创建时间,在文档插入时会自动赋值 |
@LastModifiedDate |
@LastModifiedDate :标记实体类中用于记录最后修改时间的字段 |
自动记录文档的最后修改时间,在文档更新时会自动更新 |
@Transient |
@Transient :标记实体类中的字段 |
标记实体类中的字段不进行持久化存储,仅在内存中使用 |
@PersistenceConstructor |
@PersistenceConstructor :标记构造函数 |
标记构造函数为持久化构造函数,在从数据库反序列化对象时使用 |
二、一对多关联
ItemEntity
在 MongoDB 中的存储及关联关系
public class ItemEntity {
@Field("order_ac")
@DocumentReference(lookup = "{'_id':'?#{#target}'}")
List<ItemEntity> items;
}
存储字段分析
-
_id
字段:如果没有显式指定,MongoDB 会自动生成一个唯一的ObjectId
作为_id
的值。 -
order_ac
字段:@Field("order_ac")
注解将items
字段映射到 MongoDB 文档中的order_ac
字段。在存储时,order_ac
字段存储的是关联的ItemEntity
文档的_id
信息。
关联关系分析
ItemEntity
类中的 items
字段使用了 @DocumentReference(lookup = "{'_id':'?#{#target}'}")
注解,这意味着 ItemEntity
文档内部存在一种自关联关系。具体来说,一个 ItemEntity
文档可以通过 order_ac
字段存储的 _id
关联到其他的 ItemEntity
文档。这种关联可能用于表示层级结构,比如一个商品项(ItemEntity
)可能包含多个子商品项。
存储示例
假设存在三个 ItemEntity
文档:
// 文档 1
{
"_id": "item1",
"order_ac": ["item2", "item3"]
}
// 文档 2
{
"_id": "item2",
"order_ac": []
}
// 文档 3
{
"_id": "item3",
"order_ac": []
}
在这个例子中,文档 1 通过 order_ac
字段关联到了文档 2 和文档 3,形成了一种层级关联关系。
OrderEntity
和 ItemEntity
的关联方式
public class OrderEntity {
@MongoId
String id;
@DocumentReference(lazy = true)
List<ItemEntity> items;
}
关联分析
OrderEntity
和 ItemEntity
之间是一对多的关联关系。一个 OrderEntity
对象可以关联多个 ItemEntity
对象。这种关联是通过 OrderEntity
类中的 items
字段建立的,该字段使用了 @DocumentReference(lazy = true)
注解。
关联原理
-
存储层面:当保存
OrderEntity
文档时,items
字段不会存储ItemEntity
对象的完整数据,而是存储关联的ItemEntity
文档的_id
信息。 -
查询层面:由于设置了
lazy = true
,在加载OrderEntity
文档时,不会立即去数据库中查询关联的ItemEntity
文档。只有在实际访问OrderEntity
对象的items
字段时,才会根据存储的_id
信息去数据库中查询对应的ItemEntity
文档。
存储示例
// OrderEntity 文档
{
"_id": "order1",
"items": ["item1", "item2"]
}
// ItemEntity 文档 1
{
"_id": "item1",
"order_ac": []
}
// ItemEntity 文档 2
{
"_id": "item2",
"order_ac": []
}
在这个例子中,OrderEntity
文档(_id
为 order1
)通过 items
字段关联到了两个 ItemEntity
文档(_id
分别为 item1
和 item2
)。
二、MongoRepository涉及常用方法
1. MongoRepository
自动实现方法及对应的 SQL
MongoRepository
提供了基于方法名的查询自动实现。以下是常见方法和它们的 SQL 对应关系。
MongoRepository 方法 | 功能描述 | SQL 对应 |
---|---|---|
findById(String id) |
查找指定 id 的单条记录 |
SELECT * FROM user WHERE id = '1'; |
findAll() |
查询所有记录 | SELECT * FROM user; |
save(T entity) |
插入或更新一条记录 | INSERT INTO user VALUES(...) ON DUPLICATE KEY UPDATE ...; |
deleteById(String id) |
删除指定 id 的记录 |
DELETE FROM user WHERE id = '1'; |
existsById(String id) |
判断是否存在指定 id 的记录 |
SELECT 1 FROM user WHERE id = '1' LIMIT 1; |
count() |
查询记录总数 | SELECT COUNT(*) FROM user; |
findByName(String name) |
根据名称查找用户 | SELECT * FROM user WHERE name = 'Alice'; |
findByAgeGreaterThan(int age) |
查询年龄大于指定值的用户 | SELECT * FROM user WHERE age > 30; |
findByAgeLessThan(int age) |
查询年龄小于指定值的用户 | SELECT * FROM user WHERE age < 30; |
findByNameAndAge(String name, int age) |
根据名称和年龄查找用户 | SELECT * FROM user WHERE name = 'Alice' AND age = 30; |
findAll(Pageable pageable) |
分页查询 | SELECT * FROM user LIMIT 10 OFFSET 0; |
2. 复杂查询命名规则与 SQL 对应
Spring Data MongoDB 支持通过方法名来自动生成查询。方法名遵循特定的命名规则,可以对应到 MongoDB 的查询条件。以下是常见的复杂查询命名规则及其 SQL 对应:
命名规则 | 功能描述 | SQL 对应 |
---|---|---|
findByName(String name) |
根据名称查找用户 | SELECT * FROM user WHERE name = 'Alice'; |
findByNameLike(String name) |
模糊匹配名称 | SELECT * FROM user WHERE name LIKE '%Alice%'; |
findByNameContaining(String name) |
包含指定名称 | SELECT * FROM user WHERE name LIKE '%Alice%'; |
findByAgeBetween(int start, int end) |
查询年龄在指定范围内的用户 | SELECT * FROM user WHERE age BETWEEN 25 AND 35; |
findByAgeGreaterThan(int age) |
查询年龄大于指定值的用户 | SELECT * FROM user WHERE age > 30; |
findByAgeLessThan(int age) |
查询年龄小于指定值的用户 | SELECT * FROM user WHERE age < 30; |
findByAgeGreaterThanAndAgeLessThan(int minAge, int maxAge) |
查询年龄在指定范围内的用户 | SELECT * FROM user WHERE age > 25 AND age < 35; |
findByNameOrAge(String name, int age) |
根据名称或年龄查找用户 | SELECT * FROM user WHERE name = 'Alice' OR age = 30; |
findByNameAndAgeOrAgeGreaterThan(String name, int age) |
查询符合多个条件的用户 | SELECT * FROM user WHERE name = 'Alice' AND age = 30 OR age > 40; |
3. @Query
注解规则与 SQL 对应
@Query
注解允许我们编写更灵活的查询,使用 MongoDB 查询语言(MongoDB Query Language, MQL)。在 @Query
中,你可以编写任何合法的 MongoDB 查询字符串,这些查询字符串也可以包括条件和运算符。
3.1 @Query
的常见语法
-
@Query
支持查询字符串,可以使用占位符?0
,?1
,?2
等来代替参数。 -
@Query
支持 MongoDB 的操作符,如$gt
,$lt
,$in
,$or
等。
3.2 常见的 @Query
注解使用示例
@Query 查询 |
功能描述 | SQL 对应 |
---|---|---|
@Query("{ 'age' : { '$gt' : ?0 } }") |
查询年龄大于某个值的用户 | SELECT * FROM user WHERE age > ?; |
@Query("{ 'name' : ?0, 'age' : { '$gt' : ?1 } }") |
查询指定名称且年龄大于某个值的用户 | SELECT * FROM user WHERE name = ? AND age > ?; |
@Query("{ 'age' : { '$lt' : ?0 }, 'name' : { '$regex' : ?1 } }") |
查询年龄小于某个值且名字匹配的用户 | SELECT * FROM user WHERE age < ? AND name LIKE ?; |
@Query("{ 'name' : { '$in' : ?0 } }") |
查询名称在某个集合中的用户 | SELECT * FROM user WHERE name IN (?); |
@Query("{ 'age' : { '$gte' : ?0, '$lte' : ?1 } }") |
查询年龄在指定范围内的用户 | SELECT * FROM user WHERE age BETWEEN ? AND ?; |
@Query("{ 'age' : { '$gt' : ?0 }, 'name' : { '$regex' : ?1 } }") |
查询年龄大于某个值且名字匹配的用户 | SELECT * FROM user WHERE age > ? AND name LIKE ?; |
@Query("{ 'age' : { '$gt' : ?0 }, 'name' : { '$regex' : ?1 }, 'email' : { '$exists' : true } }") |
查询年龄大于某个值,名字匹配且邮箱存在的用户 | SELECT * FROM user WHERE age > ? AND name LIKE ? AND email IS NOT NULL; |
3.3 常见的 MongoDB 查询操作符及其 SQL 类比
MongoDB 操作符 | 描述 | SQL 对应 |
---|---|---|
$gt |
大于 | > |
$gte |
大于等于 | >= |
$lt |
小于 | < |
$lte |
小于等于 | <= |
$eq |
等于 | = |
$ne |
不等于 | <> |
$in |
在某个集合中 | IN |
$nin |
不在某个集合中 | NOT IN |
$or |
或 | OR |
$and |
且 | AND |
$exists |
检查字段是否存在 | IS NOT NULL |
$regex |
正则表达式匹配 | LIKE |
聚合查询
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.group("ageRange").count().as("count")
);
AggregationResults<AgeRangeCount> results = mongoTemplate.aggregate(aggregation, "user", AgeRangeCount.class);