Spring Boot 2系列(十三):Spring Data MongoDB 集成详解与使用
MongoDB 是一款非常流行并应用于生产的分布式文档存储数据库, 数据结构非常松散,类似于 JSON 或 BSON,可以存储比较复杂的数据类型。
Spring Boot 通过 Spring Data MongoDB 项目为使用 MongoDB 提供了自动配置,Spring Data MongoDB 提供了与 MongoDB 文档数据库的集成,其关键功能领域是一个以 POJO 为中心模型,用于与 MongoDB DBCollection 交互并轻松编写 Repository 风格的数据访问层。
Spring Data MongoDB 项目将 Spring 核心概念应用于使用 MongoDB 文档数据存储的开发,提供了 template 作为存储和查询文档的高级抽象,提供了类似 JPA 的操作和注解。
Spring Boot > MongoDB , Spring Data MongoDB 项目 ,MongoDB 官网 ,MongoDB 官方文档 ,更多关于 MongoDB 的介绍和快速使用可参考MongoDB(一):Linux 环境安装MongoDB与简单使用
Spring Boot Data MongoDB
OR映射注解
提供了类似于 JPA 的注解来映射实体类与数据库之间的关系, @Id 注解是 Spring Data 提供,其它注解是 MongoDB 自有,在 org.springframework.data.mongodb.core.mapping包下。
注解 | 作用域 | 描述 |
---|---|---|
@Document | 类 | 实体对象映射 MongoDB 的数据库中的一个集合(表) |
@Id | 属性 | 此属性映射到 MongoDB 中的 ID,Spring 的注解。 |
@Indexed | 属性 | 映射当前属性是ID |
@CompoundIndex | 类 | 创建复合索引 |
@TextIndexed | 属性 | 将属性标记为文本索引的一部分 |
@Field | 属性 | 自定义字段名(实体类属性别名) |
@Version | 属性 | 字段版本,应用于乐观锁定并检保存操作的修改。 初始值为零,每次更新自动触发。 |
@Value | 属性 | 使用 SpEL 表达式从配置文件中取值,Spring 的注解。 |
@Transient | 属性 | 字段不参与持久化,不映射到文档。 默认是映射所有私有字段到文档 |
@DbRef | 属性 | 当前属性将参考其他的文档 |
可以使用 @EntityScan 注解自定义文档扫描路径。
MongoDB 配置
MongoDB 配置可以采用 Java 配置 或 XML 配置的方式。
1 | //MongoClient及MongoDbFactory |
MongoRepository
除了使用 MongoTemplate 外,Spring Data 还为 MongoDB 提供了 Repository 支持,与 JPA Repository 一样,基本原则是根据方法名自动构造查询。更多用法可参考 MongoDB Repositories。
事实上,Spring Data JPA 和 Spring Data MongoDB 共享相同的通用基础架构。与在 JPA 中的存储操作类似,假设City 现在是 Mongo 数据类而不是 JPA @Entity,它的工作方式相同,如下例所示:
1 | import org.springframework.data.domain.*; |
Spring Data MongoDB 提供了更为丰富的 Repository 操作,即 MongoRepository。需要使用时,创建实体类的 Repository 接口,继承 MongoRepository,即可调用里面的方法来操作数据。
备注: MongoRepository 没有提供 update 方法。
分页和排序接口
1
2
3
4
5
6
7public interface PagingAndSortingRepository<T, ID extends Serializable>
extends CrudRepository<T, ID> {
Iterable<T> findAll(Sort sort);
Page<T> findAll(Pageable pageable);
}例如:访问第2页,每页20条数据,示例如下:
1
2PagingAndSortingRepository<User, Long> repository = // … get access to a bean
Page<User> users = repository.findAll(PageRequest.of(1, 20));扩展的计数查询接口
1
2
3
4interface UserRepository extends CrudRepository<User, Long> {
long countByLastname(String lastname);
}扩展的条件删除接口
1
2
3
4
5
6interface UserRepository extends CrudRepository<User, Long> {
long deleteByLastname(String lastname);
List<User> removeByLastname(String lastname);
}查询操作方式
声明一个继承 Repository 的接口或扩展其子接口,传入要处理的实体类型 和 ID 类型,在接口里定义查询方法。如下示例:
1
2
3interface PersonRepository extends Repository<Person, Long> {
List<Person> findByLastname(String lastname);
}在 Java 配置文件上使用 @EnableMongoRepositories 注解启动 MongoDB 的 Repository ,可通过注解属性 basePackage 定义要扫描的包路径。
在使用时,业务层注入 PersonRepository Bean。
索引使用
下面是个复杂的映射实例类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class Person<T extends Address> {
private String id;
private Integer ssn;
private String firstName;
private String lastName;
private Integer age;
private Integer accountTotal;
private List<Account> accounts;
private T address;
public Person(Integer ssn) {
this.ssn = ssn;
}
public Person(Integer ssn, String firstName, String lastName, Integer age, T address) {
this.ssn = ssn;
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.address = address;
}
public String getId() {
return id;
}
// no setter for Id. (getter is only exposed for some unit testing)
public Integer getSsn() {
return ssn;
}
// other getters/setters omitted复合索引
复合索引对提高涉及多个字段条件的查询的性能非常重要。下面示例创建复合索引,lastName 升序、age 降序。
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Person {
private ObjectId id;
private Integer age;
private String firstName;
private String lastName;
}文本索引
创建文本索引允许将多个字段累积到可搜索的全文索引中。每个集合只能有一个文本索引,因此所有标有**@TextIndexed** 的字段都会合并到此索引中。可以对属性进行加权以影响排名结果的文档分数。
文本索引的默认语言是英语。 要更改默认语言,将语言属性设置为想要的任何语言(例如,@Document(language =“spanish”))。使用名为 language 或 @Language 的属性可以在每个文档库上定义语言覆盖。
以下示例显示如何创建文本索引并将语言设置为西班牙语:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class SomeEntity {
String foo;
String lang;
Nested nested;
}
class Nested {
String bar;
String roo;
}属性引用索引
当从 MongoDB 加载对象时,会自动解析这些引用,以便返回一个映射对象,该对象与嵌入在主文档中的对象相同。
@DBRef 提供了 lazy 属性,允许延迟解析。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Account {
private ObjectId id;
private Float total;
}
public class Person {
private ObjectId id;
private Integer ssn;
private List<Account> accounts;
}在加载 DBRef 集合时,建议将集合类型中包含的引用限制为特定的 MongoDB 集合。 这允许批量加载所有引用,而指向不同 MongoDB 集合的引用需要逐个解析。
注意:映射框架不处理级联保存。 如果更改 Person 对象引用的 Account 对象,则必须单独保存 Account 对象。 调用 Person 对象上的 save 不会自动将 Account 对象保存在 accounts 属性中。
GridFS 支持
MongoDB 还提供了一个非常好用的文件系统 GridFS ,可以在 GridFS 中存储二进制文件。 Spring Data MongoDB 提供了一个 GridFsOperations 接口以及相应的实现 GridFsTemplate,以便与文件系统进行交互。
可以通过将 MongoDbFactory 和 MongoConverter 传递给 GridFsTemplate 创建 GridFsTemplate 实例,如以下示例所示:
1 | class GridFsConfiguration extends AbstractMongoConfiguration { |
使用 GridFsTemplate 存储文件:
1 | class GridFsClient { |
store 操作需要传入 InputStream,filename 和(optionally)有关要存储的文件的元数据信息。 元数据可以是任意对象,它将由配置了 GridFsTemplate 的 MongoConverter 封装传递。也可以提供文档。
您可以通过 find() 或 **getResources() **方法从文件系统中读取文件。
find() 方法 可以传入 Query 以匹配的单个文件或多个文件。 可以使用 GridFsCriteria 帮助程序类来定义查询。 它提供静态工厂方法来封装默认元数据字段(例如 whereFilename() 和 whereContentType() )或通过 whereMetaData() 封装自定义元数据字段。 以下示例显示如何使用 GridFsTemplate 查询文件:
1 | class GridFsClient { |
注意:目前,MongoDB 不支持在从 gridfs 检索文件时定义排序标准。因此,在传递给 find() 方法的 Query 实例上定义的任何排序条件都将被忽略。
从 gridfs 读取文件还可以使用 ResourcePatternResolver 接口引入的方法。它们允许将 Ant 路径传递到方法中,从而可以检索与给定模式匹配的文件。以下示例显示如何使用 GridFsTemplate 读取文件:
1 | class GridFsClient { |
GridFsOperations 扩展了 ResourcePatternResolver,并允许将 GridFsTemplate(例如)插入到 ApplicationContext 中,以从 MongoDB 数据库中读取 Spring Config 文件。
MongoTemplate 方法
Spring Data MongoDB 提供了一个 MongoTemplate,类似于 Spring 的 JdbcTemplate 。MongoTemplate 提供了基本的数据访问的方法,Spring Boot 自动配置会将 MongoTemplate 注册为一个 Bean,在使用时直接注入即可。
MongoTemplate 是 MongoOperations 接口的,提供了大量对 MongoDB 操作的方法。
可以使用的方法如 :find,findAndModify,findAndReplace,findOne,insert,remove,save,update,和 updateMulti
查询方法
查询方法有 find,findOne,findAll,findById,findDistinct,
构建 Query 查询条件的 Criteria 类还提供了很多方法,可链式调用。
Criteria 类的方法:all,and,andOperator,elemMatch,exists,gt(范围),gte,in(in 查询),is(符合条件),lt(小于),lte,mod,ne(不等于),nin,norOperator,not,orOperator(或),regex(模糊),size(数组),type,matchingDocumentStructure。
Query 类的方法:addCriteria,fields,limit,skip,with。
保存和插入
保存方法
- **save(Object objectToSave)**:对象保存到默认集合(文档),如果存在相同的ID,则覆盖。
- **save(Object objectToSave, String collectionName)**:对象保存到指定集合,传入集合名。
插入方法
- **insert(Object objectToSave)**:对象插入到默认集合(文档),如果存在相同的ID,抛出错误。
可以将一个集合作为第一个参数,在单个批量操作中将对集保合写入到数据库。 - **insert(Object objectToSave, String collectionName)**: 对象插入指定集合,传入集合名。
另外还有个 insertAll 方法,将对象集合作为第一个参数。此方法根据前面指定的规则检查每个对象并将其插入到适当的集合中。
- **insert(Object objectToSave)**:对象插入到默认集合(文档),如果存在相同的ID,抛出错误。
注意:集合名称默认是实体类名的小写,或通过 @Document 注解自定义的集合名称。
更新操作
- updateFirst:更新查询匹配数据集中的第一条数据。
- updateMulti:更新查询匹配数据集中的所有数据。
Update 类还提供了一个静态方法 update(),可使用此静态方法来链式调用 update 的其它方法,如:addToSet,currentDate,currentTimestamp,inc(自增),max,min,multiply,pop,pull,pullAll,push,pushAll,rename,set,setOnInsert,unset
更新不存在插入
更新如果不存在则插入数据
1 | template.upsert(query(where("ssn").is(1111).and("firstName").is("Joe").and("Fraizer").is("Update")), update("address", addr), Person.class); |
查找并修改
findAndModify 方法可以更新文档并在单个操作中返回旧文档或新更新的文档。
MongoTemplate 提供了四个 findAndModify 重载方法,这些方法接受 Query 和 Update 类并将 Document 从 Document 转换为 POJO:
1 | <T> T findAndModify(Query query, Update update, Class<T> entityClass); |
findAndUpdate 操作示例:
1 | mongoTemplate.insert(new Person("Tom", 21)); |
FindAndModifyOptions 方法允许您设置 returnNew,upsert 和 remove 的选项。
1 | Query query2 = new Query(Criteria.where("firstName").is("Mary")); |
查找并替换
替换整个 Document 的最直接方法是根据 id 调用 save 方法。若没有拿到 id ,findAndReplace 提供了一种替代方法,允许通过简单查询识别要替换的文档。如下示例:
1 | Optional<User> result = template.update(Person.class) |
注意:替换内容不包含 id 本身,find 和 replace 仅替换查询条件匹配的第一个文档,取决于给定的排序顺序。
删除操作
1 | template.remove(user); |
统计查询
1 | mongoTemplate.count(query,Entity.class); |
删除数据库
1 | mongoTemplate.getDb().dropDatabase(); |
Spring Boot 集成 MongoDB
添加依赖
添加 MongoDB Start 依赖
pom.xml
1 | <dependency> |
自动配置
Spring Boot 为 MongoDB 自动配置了一个简单的 MongoDbFactory(SimpleMongoDbFactory),用于连接 MongoDB 数据库。自动配置将 MongoRepository 注册为了 Bean,直接注入即可使用;
自动配置开启了 MongoRepository ,所以可以 @EnableMongoRepositories 注解;自动配置注册了一个 MongoTemplate Bean,使用时直接注入即可使用里面的方法操作数据。
环境配置
Spring Boot 也配置了些默认的参数,如端口为:27017,服务器地址为:127.0.0.1,数据库为:test,这些参数都可以在配置文件中设置。
注意:Spring Boot 1.5.x 与 2.0.x 版本在属性配置上有些不同,不可通用。
1 | ##------------- mongodb 3.x -------------------- |
连接 MongoDB
在添加了 spring-boot-starter-data-mongodb 包后,Spring Boot 自动配置 MongoDB ,注册了 org.springframework.data.mongodb.MongoDbFactory,默认连接 mongodb://localhost/test 的服务器,当然,服务器地址可在配置文件中配置修改。
1 | # Mongo 3.0 Java 驱动配置方式 |
如果没有指定端点,默认使用 27017 端点。
如果不使用 Spring Data Mongo,你可以注入 com.mongodb.MongoClient bean 而不是使用 MongoDbFactory。 如果要完全控制建立 MongoDB 连接,还可以声明自己的 MongoDbFactory 或 MongoClient bean。
MongoDB 操作示例
示例包含了调用 MongoTemplate 的方法和 MongoRepository 的方法来执行 CRUD 的操作。
以下代码只贴下主要的CRUD代码,Github 源码
使用 MongoTemplate 操作数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
public class ActorServiceImpl implements ActorService {
private MongoTemplate mongoTemplate;
private GridFsTemplate gridFsTemplate;
/**
* 保存对象
*
* @param actor
*/
public void saveActor(Actor actor) {
mongoTemplate.save(actor);
}
/**
* 保存集合对象
*
* @param actorList
*/
public void saveActorList(List<Actor> actorList) {
mongoTemplate.insert(actorList, Actor.class);
mongoTemplate.insertAll(actorList);
}
/**
* 根据ID查询
*
* @param actorId
* @return
*/
public Actor queryByActorId(Long actorId) {
return mongoTemplate.findById(actorId, Actor.class);
}
/**
* 条件查询
*
* @param firstName
* @return
*/
public List<Actor> queryByFirstName(String firstName) {
Query query = new Query(Criteria.where("firstName").is(firstName));
return mongoTemplate.find(query, Actor.class);
}
/**
* 更新操作
*
* @param actor
* @return
*/
public UpdateResult updateActor(Actor actor) {
Query query = new Query(Criteria.where("actorId").is(actor.getActorId()));
Update update = new Update().set("firstName", actor.getFirstName());
//更新查询返回结果集的第一条
return mongoTemplate.updateFirst(query, update, Actor.class);
//更新查询返回结果集的所有
// mongoTemplate.updateMulti(query, update, Actor.class).var
}
/**
* 特殊更新,有则修改,无则添加
*
* @param actor
* @return
*/
public UpdateResult addNXupdateEX(Actor actor) {
Query query = new Query(Criteria.where("actorId").is(actor.getActorId()));
Update update = Update.update("firstName", actor.getFirstName())
.set("lastName", actor.getLastName());
return mongoTemplate.upsert(query, update, Actor.class);
}
/**
* 删除操作
*
* @param actorId
* @return
*/
public DeleteResult deleteByActorId(Long actorId) {
Query query = new Query(Criteria.where("actorId").is(actorId));
return mongoTemplate.remove(query, Actor.class);
}
/**
* 读取文件
* @throws IOException
*/
public void findFilesInGridFs() throws IOException {
Query query = new Query(Criteria.where("filename").is("logo.png"));
GridFSFile gridFSFile = gridFsTemplate.findOne(query);
GridFsResource resource = gridFsTemplate.getResource(gridFSFile);
InputStream inputStream = resource.getInputStream();
}
}Repository,使用 MongoRepository 操作数据
继承 MongoRepository,传入实体类型和 ID 类型
1
2
3
4
public interface ActorRepository extends MongoRepository<Actor, Long> {
}使用 MongoRepository 的方法操作数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class ActorServiceImpl2 {
private ActorRepository actorRepository;
/**
* 保存
* @param actor
* @return
*/
public Actor addActor(Actor actor ){
actor.setLastUpdate(new Date());
return actorRepository.save(actor);
}
/**
* 查所有
* @return
*/
public List<Actor> findAll( ){
return actorRepository.findAll();
}
/**
* 根据ID查
* @param actorId
* @return
*/
public Actor findById(Long actorId){
return actorRepository.findById(actorId).get();
}
/**
* 根据ID删除
* @param actorId
*/
public void deleteById(Long actorId){
actorRepository.deleteById(actorId);
}
//MongoRepository没有update方法
}
Spring Boot 2系列(十三):Spring Data MongoDB 集成详解与使用