Mongo基础
在Spring使用MongoDB
文档地址:https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#mongo.core
Spring 的MongoDB支持
MongoTemplate 帮助类和 MongoRepository 支持文档与POJO的对象映射
基本配置
Maven
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>3.0.3.RELEASE</version>
</dependency>日志
log4j.category.org.springframework.data.mongodb=DEBUG
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %40.40c:%4L - %m%n主从配置
参考链接:https://docs.spring.io/spring-boot/docs/2.3.3.RELEASE/reference/htmlsingle/#boot-features-connecting-to-mongodb
使用 spring.data.mongodb.uri 进行replicaset的配置
spring.data.mongodb.uri=mongodb://user:secret@mongo1.example.com:12345,mongo2.example.com:23456/test不使用主从时使用以下配置:
spring.data.mongodb.host=mongoserver.example.com
spring.data.mongodb.port=27017
spring.data.mongodb.database=test
spring.data.mongodb.username=user
spring.data.mongodb.password=secret文档地址: https://docs.mongodb.com/manual/reference/connection-string/
标准URI String 格式
mongodb://[username:password@]host1[:port1][,...hostN[:portN]][/[defaultauthdb][?options]]选项配置 https://docs.mongodb.com/manual/reference/connection-string/#connections-connection-options
例子 复制集配置:
mongodb://db1.example.net:27017,db2.example.net:2500/?replicaSet=test&connectTimeoutMS=300000分片集群:
mongodb://mongos0.example.com:27017,mongos1.example.com:27017,mongos2.example.com:27017Mongo shell
显示当前数据库
db选择数据库
use (database name)备份与恢复
mongo dumpmongodump --username username --password userpassword --db databaseMongo restoremongorestore --dir database-dump/ -u username -p userpassword
数据类型
MongoDB在保留了JSON k-v对特性的同时增加了其它数据类型的支持。
总览
_id 和 ObjectIds
MongoDB内每个文档都必需含有 "_id"这个键,"_id"可以是任意类型,默认是ObjectId。单个集合内的文档必须有唯一的id,不同集合间可以相同。
ObjectId
- 是 "_id"的默认值
- 轻量级,容易生成全局唯一值
- 因为MongoDB为分布式而设计,所以不仅仅使用自增
- 占用12bytes,换成16进制就是长度为24的16进制字符串
ObjectId 格式:
- 4 bytes:timestamp
- 3 bytes: 机器的唯一标签,一般是hostname的hash
- 2 bytes:进程的PID
- 3 bytes:自增计数器,单个进程最多256^3个唯一ObjectId
MongoTemplate
- 线程安全,可以多个实例复用
- 对象与文档的映射通过MongoConverter完成,Spring提供 MappingMongoConverter,也可以自己写。
查询
基本使用示例:查询与分页
import static org.springframework.data.mongodb.core.query.Criteria.where;
import static org.springframework.data.mongodb.core.query.Update.update;
import static org.springframework.data.mongodb.core.query.Query.query;
public Page<MySuperUserDto> getVipUserList(int size, int page) {
// 查询,排序
Sort sort = new Sort(Sort.Direction.DESC, "bsUserId");
// 分页,添加排序
Pageable pageable = PageRequest.of(page, size, sort);
List<UserPo> userList = mongoTemplate.find(
query(where("BBBBBB")
.is("TTTTTT"))
.with(pageable)
, UserPo.class);
userList.forEach(user -> log.info("user: " + user));
// 正则表达式
userList = mongoTemplate.find(
query(where("clientType")
.regex("TTTTTT.*?"))
.with(pageable)
, UserPo.class);
userList.forEach(user -> log.info("user: " + user));
// 总数
long count = mongoTemplate.count(query(where("clientType")
.regex("TTTTTT.*?"))
.with(pageable)
, UserPo.class);
log.info("Record count: " + count);
List<MySuperUserDto> userDtoList = VipUserMapper.INSTANCE.superUserToDto(userList);
// 分页实现
Page<MySuperUserDto> dtoPage = new PageImpl<>(userDtoList, pageable, count);
// 获取总页数
dtoPage.getTotalPages();
// 获取总记录数
dtoPage.getTotalElements();
log.info("Page info: " + dtoPage);
// 使用 limit 和 skip的分页方式
userList = mongoTemplate.find(
query(where("clientType")
.regex("TTTTTT.*?"))
.skip(size * page)
.limit(size)
, UserPo.class);
userList.forEach(user -> log.info("user: " + user));
userDtoList = VipUserMapper.INSTANCE.superUserToDto(userList);
}插入
insert: Inserts an object. If there is an existing document with the same id, an error is generated.
insertAll: Takes a Collection of objects as the first parameter. This method inspects each object and inserts it into the appropriate collection, based on the rules specified earlier.
save: Saves the object, overwriting any object that might have the same id.
更新 update
- updateFirst
支持使用upsert(),即当没有找到文档的时候,执行插入操作
- updateMulti
Update支持MongoDB的update modifier,详情看文档
- findAndModify
支持更新文档后返回旧的或是新的文档,使用示例:
Query query = new Query(Criteria.where("firstName").is("Harry"));
Update update = new Update().inc("age", 1);
Person oldValue = template.update(Person.class)
.matching(query)
.apply(update)
.findAndModifyValue(); // return's old person object
assertThat(oldValue.getFirstName()).isEqualTo("Harry");
assertThat(oldValue.getAge()).isEqualTo(23);
Person newValue = template.query(Person.class)
.matching(query)
.findOneValue();
assertThat(newValue.getAge()).isEqualTo(24);
Person newestValue = template.update(Person.class)
.matching(query)
.apply(update)
.withOptions(FindAndModifyOptions.options().returnNew(true)) // Now return the newly updated document when updating
.findAndModifyValue();
assertThat(newestValue.getAge()).isEqualTo(25);更多选项
Person upserted = template.update(Person.class)
.matching(new Query(Criteria.where("firstName").is("Mary")))
.apply(update)
.withOptions(FindAndModifyOptions.options().upsert(true).returnNew(true))
.findAndModifyValue()Aggregation更新
使用案例,按范围设定值
AggregationUpdate update = Aggregation.newUpdate()
.set("average").toValue(ArithmeticOperators.valueOf("tests").avg())
.set("grade").toValue(ConditionalOperators.switchCases(
when(valueOf("average").greaterThanEqualToValue(90)).then("A"),
when(valueOf("average").greaterThanEqualToValue(80)).then("B"),
when(valueOf("average").greaterThanEqualToValue(70)).then("C"),
when(valueOf("average").greaterThanEqualToValue(60)).then("D"))
.defaultTo("F")
);
template.update(Student.class)
.apply(update)
.all();findAndReplace
Optional<User> result = template.update(Person.class)
.matching(query(where("firstame").is("Tom")))
.replaceWith(new Person("Dick"))
// 提示:用于replace的对象不要有id,因为替换操作后,旧文档的id会给到新文档上
.withOptions(FindAndReplaceOptions.options().upsert())
// 如果没有就进行插入
.as(User.class)
// 支持映射到其它对像
.findAndReplace();乐观锁
要启用乐观锁,需要在类里面添加 @Version Long version;。 示例
@Document
class Person {
@Id String id;
String firstname;
String lastname;
@Version Long version;
}
Person daenerys = template.insert(new Person("Daenerys"));
// 此时version = 0
Person tmp = template.findOne(query(where("id").is(daenerys.getId())), Person.class);
// 此时version = 0
daenerys.setLastname("Targaryen"); // 此时version = 1
template.save(daenerys);
template.save(tmp); // throws OptimisticLockingFailureException删除 delete
类型映射
_id的映射
- 如果没有提供,将会自动生成
- 添加了@Id注解将映射为_id
- 命名为id的对象将映射为_id
类型转换规则
- 字符串:如果使用 Converter<String, ObjectId> 可以成功转为 ObjectId, 则存储为ObjectId,否则以字符串格式存储。
- BigInteger用Converter<BigInteger, ObjectId>转换成ObjectId
@MongoId注解 如果数据没有通过Spring Data映射层,该注解提供更多可定制选项
public class PlainStringId {
@MongoId String id; // The id is treated as String without further conversion.
}
public class PlainObjectId {
@MongoId ObjectId id; // The id is treated as ObjectId
}
public class StringToObjectId {
@MongoId(FieldType.OBJECT_ID) String id; // The id is treated as ObjectId if the given String is a valid ObjectId hex, otherwise as String. Corresponds to @Id usage.
}继续: https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#mongo-template 11.5.2
类型映射
@TypeAlias 用于不把Java类名写成类型信息而使用key
@TypeAlias("pers")
class Person {}