资料集成
MongoDB 官网: https://www.mongodb.com
MongoDB DOCS: https://docs.mongodb.com
Spring Data MongoDB DOCS: https://docs.spring.io/spring-data/data-mongo/docs/
常规
MongoDB 访问: localhost:27017
MongoDB WEB 访问: localhost:28017
命令
参考:
https://docs.mongodb.com/manual/reference/operator/
https://docs.mongodb.com/manual/crud/
...
show dbs;
use admin;
show collections;
// userMO 文档
db.userMO.find();
db.userMO.find().count();
db.userMO.find({"_id" : ""}).count();
...
Spring Boot 集成
pom.xml 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
或者
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
</dependency>
参考:
Spring Data MongoDB: http://projects.spring.io/spring-data-mongodb/
application.properties 配置
spring.data.mongodb.uri=mongodb://name:pass@localhost:27017/database
模拟实体类
import lombok.Data;
@Data
public class UserMO {
public UserMO () {
}
public UserMO (String id, String name, int age, String phone) {
this.id = id;
this.name = name;
this.age = age;
this.phone = phone;
}
/**
* 主键
*/
private String id;
/**
* 姓名
*/
private String name;
/**
* 年龄
*/
private ing age;
/**
* 手机号
*/
private String phone;
/**
* 卡牌库
*/
private List<CardMO> cards = new ArrayList<>();
}
import lombok.Data;
@Data
public class CardMO {
public CardMO () {
}
public CardMO (String id, String name, int number) {
this.id = id;
this.name = name;
this.number = number;
}
/**
* 主键
*/
private String id;
/**
* 卡牌名称
*/
private String name;
/**
* 数量
*/
private ing number;
}
基本操作
@Autowired
private MongoTemplate mongoTemplate;
增加
UserMO userMO = new UserMO("user-00", "贱贱", 27, "137 xxxx xxxx");
userMO.getCards().add(new CardMO("card-cavalry-00", "骑兵", 1));
userMO.getCards().add(new CardMO("card-infantry-00", "步兵", 1));
userMO.getCards().add(new CardMO("card-archer-00", "弓箭手", 1));
// save() 根据 _id 判断, 没有则插入, 有则覆盖
mongoTemplate.save(userMO);
// insert() 根据 _id 判断, 没有则插入, 有则会抛错
mongoTemplate.insert(userMO);
删除
mongoTemplate.remove(new Query(Criteria.where("id").is(userId)), UserMO.class);
删除位于 cards 集合里面的数据 (updateMulti 是 更新多条)
Update update = new Update();
update.pull("cards", new BasicDBObject("id", cardMO.getId()));
mongoTemplate.updateMulti(new Query(Criteria.where("id").is(userId)), update, UserMO.class);
更新
Query query = new Query(Criteria.where("id").is(userMO.getId()));
// 复杂版 (就是每一个属性都手动 set)
Update update = new Update();
update.set("name", "小贱贱");
update.set("age", 28);
// 简化版 (就是利用 Java 反射实现 set)
Update update = MongoHelper.update(userMO);
WriteResult writeResult = mongoTemplate.updateFirst(query, update, UserMO.class);
更新 UserMO.cards 中的数据:
Query query = new Query(Criteria.where("id").is(userMO.getId()).and("cards.id").is(card.getId()));
Update update = new Update();
update.set(String.format("cards.$.%s", "id"), "card-cavalry-00");
update.set(String.format("cards.$.%s", "name"), "骑兵-00");
WriteResult writeResult = mongoTemplate.updateFirst(query, update, CardMO.class);
存在则更新, 不存在则增加
Update update = new Update();
update.addToSet("cards", cardMO);
WriteResult _writeResult = mongoTemplate.upsert(new Query(Criteria.where("id").is(userMO.getId())), update, UserMO.class);
查询
查询一条
UserMO userMO = mongoTemplate.findOne(new Query(Criteria.where("id").is(userId)), UserMO.class);
查询列表
List<UserMO> userMOs = mongoTemplate.find(new Query(Criteria.where("age").is(27)), UserMO.class);
过滤属性不存在
Criteria criteria = Criteria.where("id").is(userId);
criteria.and("userId").is(new BasicDBObject("$exists", false));
List<UserMO> userMOs = mongoTemplate.find(new Query(criteria), UserMO.class);
分页, 排序
Query query = new Query(criteria);
int total = (int) mongoTemplate.count(query, UserMO.class);
// desc, asc
query.with(new Sort("desc", "id"));
query.skip(0).limit(20);
List<UserMO> userMOs = mongoTemplate.find(query, UserMO.class);
辅助小工具类
import com.fasterxml.jackson.annotation.JsonIgnore;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class MongoHelper {
public static Map<String, Object> update (Object object) {
Map<String, Object> update = new HashMap<>();
try {
Field [] fields = object.getClass().getDeclaredFields();
for(Field field : fields) {
// 属性忽略
JsonIgnore jsonIgnore = field.getAnnotation(JsonIgnore.class);
if(jsonIgnore != null) {
continue;
}
boolean isAccessible = field.isAccessible();
field.setAccessible(true);
String fieldName = field.getName();
Object fieldValue = field.get(object);
if(fieldName != null && fieldValue != null) {
update.put(fieldName, fieldValue);
}
field.setAccessible(isAccessible);
}
} catch (Exception e) {
e.printStackTrace();
}
return update;
}
}
命令 | 代表含义 |
---|---|
$lt | < |
$lte | <= |
$gt | > |
$gte | >= |
$ne | != |
使用经验
1. 树形结构 (应用于 菜单, 组织架构 等)
Model Tree Structures: https://docs.mongodb.com/manual/applications/data-models-tree-structures/
我的实现结构选择的是:
Model Tree Structures with Child References: https://docs.mongodb.com/manual/applications/data-models-tree-structures/
2. 金额 类型 BigDecimal
注意:
2.1. 对于 BigDecimal 类型保存到 MongoDB 中是 字符串
2.2. BigDecimal 没有 空构造函数
AggregationResults<BigDecimal> aggregationResults = mongoTemplate.aggregate(aggregation, COLLECTION_NAME, BigDecimal.class);
这样会提示:
org.springframework.data.mapping.model.MappingInstantiationException: Failed to instantiate java.math.BigDecimal using constructor NO_CONSTRUCTOR with arguments
3. 过滤条件限制某个属性不存在
criteria.and("userId").is(new BasicDBObject("$exists", false));
4. 日期类型
if (condition.getCreatedTimeFrom() != null && condition.getCreatedTimeTo() != null) {
criteria.and("createdTime").gte(condition.getCreatedTimeFrom()).lt(condition.getCreatedTimeTo());
} else if (condition.getCreatedTimeFrom() != null) {
criteria.and("createdTime").gte(condition.getCreatedTimeFrom());
} else if (condition.getCreatedTimeTo() != null) {
criteria.and("createdTime").lt(condition.getCreatedTimeTo());
}
踩坑经验
1. 根据 CardMO 的 主键 ID 删除 UserMO 实体对象中 cards 中的 CardMO 数据
MongoDB 文档: https://docs.mongodb.com/manual/reference/operator/update/pull/
使用 Spring Data MongoDB 操作:
Query query = new Query(Criteria.where("id").is("1100004083497709568"));
Update update = new Update().pull("cards", new BasicDBObject("id", "1100487509678071808"));
WriteResult writeResult = mongoTemplate.updateMulti(query, update, UserMO.class);
2. 并发问题一定要注意
当涉及消费MQ消息操作MongoDB的时候, 一定要注意并发问题, 可以加分布式锁