MongoDB学习02:使用MongoTemplate操作MongoDB
- `MongoTemplate`的使用示例
- `MongoTemplate`的使用
- 向Spring容器注入`MongoTemplate`
- 字段和类型映射
- `_id`字段映射
- `_class`字段映射
- 自定义类型映射
- 使用`MongoTemplate`进行CRUD
- 保存(`insert`/`save`)文档的方法
- 查询(`query`)文档的方法
- 修改(`update`)文档的方法
- 更新插入(`upsert`)文档的方法
- 查询并修改(`find and modify`)文档的方法
- 查询并删除(`find and remove`)文档的方法
- 查询并替换(`find and replace`)文档的方法
SpringData的官方文档Spring Data MongoDB - Reference Documentation,阅读官方文档永远是最好的学习方法.
MongoTemplate
的使用示例
- 创建SpringBoot项目,在
pom.xml
中添加依赖如下:
<dependency>
<!-- 引入Spring操作MongoDB的库 -->
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
</dependency>
- 在
cn.maoritian.domain
包下创建实体类Preson
如下:
package cn.maoritian.domain;
public class Person {
private String id;
private String name;
private int age;
public String getId() {return id; }
public String getName() {return name; }
public int getAge() {return age; }
public Person(String name, int age) {this.name = name; this.age = age; }
@Override
public String toString() {return "Person [id=" + id + ", name=" + name + ", age=" + age + "]"; }
}
- 在
cn.maoritian
包下创建SpringBoot启动类MongoApp
,在其main()
函数中进行对MongoDB的操作
package cn.maoritian;
import static org.springframework.data.mongodb.core.query.Criteria.where;
import cn.maoritian.domain.Person;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;
import com.mongodb.MongoClient;
public class MongoApp {
private static final Log log = LogFactory.getLog(MongoApp.class);
public static void main(String[] args) throws Exception {
MongoOperations mongoOps = new MongoTemplate(new MongoClient(), "数据库名");
mongoOps.insert(new Person("Joe", 34)); // 默认插入进名为person的collection
log.info(mongoOps.findOne(new Query(where("name").is("Joe")), Person.class));
mongoOps.dropCollection("person");
}
}
程序输出如下:
22:35:59.212 [main] DEBUG org.springframework.data.mongodb.core.index.MongoPersistentEntityIndexCreator - Analyzing class class cn.maoritian.domain.Person for index information.
22:35:59.260 [main] DEBUG org.springframework.data.mongodb.core.MongoTemplate - Inserting Document containing fields: [name, age, _class] in collection: person
22:35:59.337 [main] DEBUG org.springframework.data.mongodb.core.MongoTemplate - findOne using query: { "name" : "Joe" } fields: Document{{}} for class: class cn.maoritian.domain.Person in collection: person
22:35:59.372 [main] INFO cn.maoritian.MongoApp - Person [id=5d7e4c39676d91262cdf734f, name=Joe, age=34]
22:35:59.384 [main] DEBUG framework.data.mongodb.core.MongoTemplate: 375 - Dropped collection [database.person]
MongoTemplate
的使用
向Spring容器注入MongoTemplate
下面三种方式均可向Spring容器中注入MongoTemplate
:
- 使用
.xml
文件注入:
<mongo:mongo-client host="localhost" port="27017"/>
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg ref="mongoClient"/>
<constructor-arg name="databaseName" value="数据库名"/>
</bean>
- 使用Java Config注入:
@Configuration
public class AppConfig {
public @Bean MongoClient mongoClient() {
return new MongoClient("localhost");
}
public @Bean MongoTemplate mongoTemplate() {
return new MongoTemplate(mongoClient(), "数据库名");
}
}
- 在SpringBoot项目中,还可以使用
.yml
文件注入
spring:
data:
mongodb:
uri: mongodb://服务器IP地址:端口号/数据库名
其中,mongodb属性对应的所有yml配置项均为org.springframework.boot.autoconfigure.mongo.MongoProperties
类的属性,其源代码如下:
package org.springframework.boot.autoconfigure.mongo;
import com.mongodb.MongoClientURI;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "spring.data.mongodb") // 指明yml配置的前缀
public class MongoProperties {
public static final int DEFAULT_PORT = 27017;
public static final String DEFAULT_URI = "mongodb://localhost/test";
private String host;
private Integer port = null;
private String uri;
private String database;
private String authenticationDatabase;
private String gridFsDatabase;
private String username;
private char[] password;
private Class<?> fieldNamingStrategy;
// get,set方法...
}
字段和类型映射
_id
字段映射
MongoDB要求每个文档包含一个_id
字段,在实体类中,如下两种属性会被映射为_id
字段:
- 使用
@Id
注解的属性会被映射为数据库_id
字段. - 名为
id
的属性会被映射为数据库_id
字段.
_class
字段映射
当一个POJO对象被转换为MongoDB中的文档时,会自动在文档末尾加入一个_class
字段,其值为该POJO类的全限定类名(fully qualified classname
).
下面例子展示一个实体类与其转化为的MongoDB文档间的关系:
public class Sample {
Contact value;
}
public abstract class Contact { … }
public class Person extends Contact { … }
Sample sample = new Sample();
sample.value = new Person();
mongoTemplate.save(sample);
{
"value" : { "_class" : "cn.maoritian.Person" },
"_class" : "cn.maoritian.Sample"
}
如果不想在_class
字段中保存整个类名,可以在POJO类上使用@TypeAlias
注解,其值为_class
字段的值.
@TypeAlias("pers")
class Person {
...
}
{
"value" : { "_class" : "pers" },
"_class" : "cn.maoritian.Sample"
}
自定义类型映射
通过向Spring容器中注入MongoTypeMapper
的实现类,我们可以配置自定义类型映射.
class CustomMongoTypeMapper extends DefaultMongoTypeMapper {
//implement custom type mapping here
}
@Configuration
class SampleMongoConfiguration extends AbstractMongoConfiguration {
@Override
protected String getDatabaseName() {
return "数据库名";
}
@Override
public MongoClient mongoClient() {
return new MongoClient();
}
@Bean
@Override
public MappingMongoConverter mappingMongoConverter() throws Exception {
MappingMongoConverter mmc = super.mappingMongoConverter();
mmc.setTypeMapper(customTypeMapper());
return mmc;
}
@Bean
public MongoTypeMapper customTypeMapper() {
return new CustomMongoTypeMapper();
}
}
使用MongoTemplate
进行CRUD
对于每个实体类,MongoTemplate
会将其对象存入一个单独的集合(collection
)中,该集合的默认集合名为实体类名首字母变小写(如cn.maoritian.Person
类的所有实例默认会被存入person
集合中).
可以在实体类上加以@Document
注解,其collection
属性指定将该类的实例对象存入的集合名.
@Document(collection = "集合名")
public class Person {
// ...
}
保存(insert
/save
)文档的方法
下面三个方法可以向MongoDB中保存文档
-
insert(Object objectToSave)
和insert(Object objectToSave, String collectionName)
: 将objectToSave
存入MongoDB中,若与集合中现有文档发生_id
字段冲突,则报错. -
insertAll(Collection<Object> objectsToSave)
: 将集合objectsToSave
中的所有对象存入MongoDB中,若与集合中现有文档发生_id
字段冲突,则报错. -
save(Object objectToSave)
和save(Object objectToSave, String collectionName)
: 将objectToSave
存入MongoDB中,若与集合中现有文档发生_id
字段冲突,则覆盖之前的文档.
查询(query
)文档的方法
下面五个方法可以查询MongoDB中的文档
-
<T> List<T> findAll(Class<T> entityClass)
和<T> List<T> findAll(Class<T> entityClass, String collectionName)
: 查询所有T类型的文档. -
<T> T findOne(Query query, Class<T> entityClass)
和T findOne(Query query, Class<T> entityClass, String collectionName)
: 查询一个满足query
条件的文档. -
<T> T findById(Object id, Class<T> entityClass)
和<T> T findById(Object id, Class<T> entityClass, String collectionName)
: 通过id
查询文档. -
<T> List<T> find(Query query, Class<T> entityClass)
和<T> List<T> find(Query query, Class<T> entityClass, String collectionName)
: 查询所有满足query
条件的文档.
List<Person> result = mongoTemplate.find(query(where("age").lt(50)
.and("accounts.balance").gt(1000.00d)), Person.class);
通过Query.addCriteria()
方法,我们可以使用Criteria
对象构造Query
对象.Criteria
的方法与MongoDB的操作符一一对应如下:
| 对应的MongoDB操作符 |
|
|
| 对 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| field matching( |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
修改(update
)文档的方法
下面两个方法可以修改MongoDB中的文档
-
updateFirst(Query query, Update update, Class<?> entityClass)
和updateFirst(Query query, Update update, String collectionName)
: 修改第一个满足条件的文档. -
updateMulti(Query query, Update update, Class<?> entityClass)
和updateMulti(Query query, Update update, String collectionName)
: 修改第一个满足条件的文档.
参数的意义如下:
-
query
: 表示查询条件 -
update
: 表示修改操作
import static org.springframework.data.mongodb.core.query.Criteria.where;
import static org.springframework.data.mongodb.core.query.Query;
import static org.springframework.data.mongodb.core.query.Update;
WriteResult wr = mongoTemplate.updateMulti(new Query(where("accounts.accountType").is(Account.Type.SAVINGS)),
new Update().inc("accounts.$.balance", 50.00), Account.class);
Update
对象可以通过链式调用添加修改操作.常见的方法如下,与MongoDB的修改操作符有一一对应的关系
| 对应的MongoDB操作符 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
下面四个例子展示了Update
对象和MongoDB操作符的对应关系:
new Update().push("category").each("spring", "data")
new Update().push("key").atPosition(Position.FIRST).each(Arrays.asList("Arya", "Arry", "Weasel"));
new Update().push("key").slice(5).each(Arrays.asList("Arya", "Arry", "Weasel"));
new Update().addToSet("values").each("spring", "data", "mongodb");
{ $push : { "category" : { "$each" : [ "spring" , "data" ] } } }
{ $push : { "key" : { "$position" : 0 , "$each" : [ "Arya" , "Arry" , "Weasel" ] } } }
{ $push : { "key" : { "$slice" : 5 , "$each" : [ "Arya" , "Arry" , "Weasel" ] } } }
{ $addToSet : { "values" : { "$each" : [ "spring" , "data" , "mongodb" ] } } }
更新插入(upsert
)文档的方法
更新插入upsert(Query query, Update update, Class<?> entityClass)
和upsert(Query query, Update update, String collectionName)
方法,执行的是保存或修改过程.
- 若根据
query
条件能查到文档,则执行updateFirst()
操作 - 若根据
query
条件不能查到文档,则执行insert()
操作
mongoTemplate.upsert(query(where("ssn").is(1111).and("firstName").is("Joe").and("Fraizer").is("Update")),
update("address", addr), Person.class);
查询并修改(find and modify
)文档的方法
查询并修改find and modify
有如下四个重载方法:
<T> T findAndModify(Query query, Update update, Class<T> entityClass);
<T> T findAndModify(Query query, Update update, Class<T> entityClass, String collectionName);
<T> T findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass);
<T> T findAndModify(Query query, Update update, FindAndModifyOptions options, Class<T> entityClass, String collectionName);
其中options
参数封装了修改操作的一些选项如下:
-
returnNew
属性取值true|false
,默认值为false
.表示返回的是修改前的查询结果还是修改后的结果 -
upsert
属性取值true|false
,默认值为false
.取值为true
表示执行upsert
操作;取值为false
表示执行update
操作. -
remove
属性取值true|false
,默认值为false
.表示是否删除掉查询结果
mongoTemplate.insert(new Person("Harry", 23));
Query query1 = new Query(Criteria.where("firstName").is("Harry"));
Update update1 = new Update().inc("age", 1);
Person p1 = mongoTemplate.findAndModify(query1, update1, Person.class);
// 得到 Person [id=5d90533e74fa159cdb13cae0, firstName=Harry, age=1]
Query query2 = new Query(Criteria.where("firstName").is("Mary"));
Update update2 = new Update().inc("age", 1);
Person p2 = mongoTemplate.findAndModify(query2, update2, new FindAndModifyOptions().returnNew(true).upsert(true), Person.class);
// 得到 Person [id=5d9054b574fa159cdb13cae1, firstName=Mary, age=1]
查询并删除(find and remove
)文档的方法
查询并删除文档的方法findAndRemove()
与findAndModify()
的使用几乎完全相同,有以下几个重载的方法:
<T> List<T> findAllAndRemove(Query query, String collectionName)
<T> List<T> findAllAndRemove(Query query, Class<T> entityClass)
<T> List<T> findAllAndRemove(Query query, Class<T> entityClass, String collectionName)
查询并替换(find and replace
)文档的方法
与findAndModify()
方法类似,findAndReplace()
方法也需要指定一个option
Optional<User> result = template.update(Person.class)
.matching(query(where("firstame").is("Tom")))
.replaceWith(new Person("Dick"))
.withOptions(FindAndReplaceOptions.options().upsert())
.as(User.class)
.findAndReplace();