一、MongoDB简介
1.1、什么是MongoDB?
MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,且与关系数据库的最为相像的。它是一个文档数据库,它的数据以文档方式进行存储,将数据存储在类似 JSON 的 BSON 文档中。支持的数据结构非常松散,因此可以存储比较复杂的数据类型。
1.2、MongoDB有什么特点?
Mongo 最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。
二、MongoDB基本概念
2.1、文档(Document)
文档是MongoDB中最基本的数据单元,由键值对组成,类似于 JSON 格式,可以存储不同字段,字段的值可以包括其他文档,数组和文档数组。
2.2、集合(Collection)
集合指的是文档组(类似于MySql中表的概念),里面可以存储许多文档。
2.3、数据库(Database)
MongoDB中可以存在多个数据库,每个数据库中可以设置不同的集合与用户权限。
2.4、数据类型
数据类型 | 说明 |
String | 字符串,存储数据常用的数据类型。在 MongoDB 中,UTF-8 编码的字符串才是合法的 |
Integer | 整型数值,用于存储数值。根据你所采用的服务器,可分为 32 位或 64 位 |
Boolean | 布尔值,用于存储布尔值(true/false) |
Double | 双精度浮点值,用于存储浮点值 |
Min/Max keys | 将一个值与 BSON(二进制的 JSON)元素的最低值和最高值相对比 |
Array | 用于将数组或列表或多个值存储为一个键 |
Timestamp | 时间戳。记录文档修改或添加的具体时间 |
Object | 用于内嵌文档 |
Null | 用于创建空值 |
Symbol | 符号。该数据类型基本上等同于字符串类型,但不同的是,它一般用于采用特殊符号类型的语言 |
Date | 日期时间,用 UNIX 时间格式来存储当前日期或时间。你可以指定自己的日期时间:创建 Date 对象,传入年月日信息 |
Object ID | 对象 ID,用于创建文档的 ID |
Binary Data | 二进制数据,用于存储二进制数据 |
Code | 代码类型,用于在文档中存储 JavaScript 代码 |
Regular expression | 正则表达式类型,用于存储正则表达式 |
三、安装运行
3.1、镜像拉取
- MongoDB 镜像库地址:https://hub.docker.com/_/mongo?tab=tags&page=1
- 拉取镜像命令:
java docker pull mongo:latest
3.2、服务运行
- 跑服务:
java docker run -itd --name mongo -p 27017:27017 mongo --auth
- 进入容器:
java docker exec -it 542d12efed26 mongo
注意事项:–auth:需要密码才能访问容器服务。进入容器命令后方固定为“mongo”,类似mysqld那种。
四、Shell操作步骤
4.1、进入mongo命令端
java docker exec -it xxxxx mongo
进入容器后,默认数据库是test。可通过use命令进行数据库切换。若数据库存在则切换,不存在则创建。
4.2、选择数据库
java use admin
4.3、创建用户、设置权限
查看系统所有用户:java db.system.users.find().pretty()
如下所示:
"_id" : "admin.root",
"userId" : UUID("704ba779-237a-43f3-b6fc-3626d7b9b5df"),
"user" : "root",
"db" : "admin",
"credentials" : {
"SCRAM-SHA-1" : {
"iterationCount" : 10000,
"salt" : "44bA6kZd0Yd53zfgUeW03Q==",
"storedKey" : "hsvwvWX9Dr/4tUf3WvJACE6SZx0=",
"serverKey" : "gPirvqO4QV2aaRZjmdf0fDV9s5s="
},
"SCRAM-SHA-256" : {
"iterationCount" : 15000,
"salt" : "th+fxRfSohujGXDuK/pGhXlxABRmYpqXLsNSTg==",
"storedKey" : "jPcM5bBFJGT+6s32CWag7lokhKaVJ+9nb1ITWKDXqVc=",
"serverKey" : "yS1qMIY2LcXtuak6pR8y8evFyhmYMDs2PCq9ZBEmifA="
}
},
"roles" : [
{
"role" : "userAdminAnyDatabase",
"db" : "admin"
}
]
}
{
"_id" : "admin.wss",
"userId" : UUID("77829777-c420-4522-857c-99fc448108a0"),
"user" : "wss",
"db" : "admin",
"credentials" : {
"SCRAM-SHA-1" : {
"iterationCount" : 10000,
"salt" : "2Wnr6DI1b/Sjgy5+LZ7NxQ==",
"storedKey" : "fsBO/wNIlBncdT3FL6B221bwZg0=",
"serverKey" : "9hBHcJtcs2bHqYbsG8Ew8u5dQfM="
},
"SCRAM-SHA-256" : {
"iterationCount" : 15000,
"salt" : "darr3ESorPMuvEU24SCg/5YseiMZVK4qFrQm/A==",
"storedKey" : "zRGMLhJOgWwwdSeE41GLC0WjYoUqYtSp12enBvGCbJM=",
"serverKey" : "wWFnAY4azewtiUAUw1UpfIZPWR8OVHodTU4Run38PQo="
}
},
"roles" : [
{
"role" : "readWrite",
"db" : "mydb"
}
]
}
{
"_id" : "admin.wshanshi",
"userId" : UUID("6711d8a1-506e-42cf-ae51-4c69d86a8b4b"),
"user" : "wshanshi",
"db" : "admin",
"credentials" : {
"SCRAM-SHA-1" : {
"iterationCount" : 10000,
"salt" : "ew/kf1zjKSIaI2+72D4X8A==",
"storedKey" : "t4WfIMbMUXRuzodhvAU07riBag8=",
"serverKey" : "TqtrkmU8mGbOGzD6QI1X2WAR7gE="
},
"SCRAM-SHA-256" : {
"iterationCount" : 15000,
"salt" : "g0ibGWeNHRAYybgfZOepxiJfOd4hGfkdaF5/tw==",
"storedKey" : "N5/1Bq8oEZC7JHIT+Df92IwNu0n0Pf19w2iSNLk5DwE=",
"serverKey" : "o2uFVEc1ZS4Dr5OkGbF2kd51PLq96CBFgHeanJ1Yr4Y="
}
},
"roles" : [
{
"role" : "root",
"db" : "admin"
}
]
}
{
"_id" : "testtwst.admin",
"userId" : UUID("55fa4589-ebff-4d7d-8968-9e5be0547423"),
"user" : "admin",
"db" : "testtwst",
"credentials" : {
"SCRAM-SHA-1" : {
"iterationCount" : 10000,
"salt" : "+YyskVWzqq9VPrEDvWp29Q==",
"storedKey" : "tPo3ELQgP+NzYElbA5ZqVhZFa8Y=",
"serverKey" : "XzArOMmOA0pxtocf+8pFbND7x/s="
},
"SCRAM-SHA-256" : {
"iterationCount" : 15000,
"salt" : "hGnjSaUEyUtHOK6JKfjGw/O1Gon7egL3nY+oMQ==",
"storedKey" : "KtD4D/44mORZT83OFiwNZ/3TdYIJHtzG90ZU+HmOJOM=",
"serverKey" : "XEI2anD0NYcomPGwwUZxKcMvbI8FN48gLnmgPprJiHI="
}
},
"roles" : [
{
"role" : "root",
"db" : "admin"
}
]
}
设置用户账户权限:
java db.createUser({ user: 'wss', pwd: 'wss', roles: [ { role: "root", db: "admin" } ] });
以上操作就相当于创建了一个用户。上面创建的用户角色是超级账号,是超级用户,role后面的参数可以参考下面:
角色 | 说明 |
read | 允许用户读取指定数据库 |
readWrite | 允许用户读写指定数据库 |
dbAdmin | 允许用户在指定数据库中执行管理函数,如索引创建、删除,查看统计或访问system.profile |
userAdmin | 允许用户向system.users集合写入,可以找指定数据库里创建、删除和管理用户 |
clusterAdmin | 只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限 |
readAnyDatabase | 只在admin数据库中可用,赋予用户所有数据库的读权限 |
readWriteAnyDatabase | 只在admin数据库中可用,赋予用户所有数据库的读写权限 |
userAdminAnyDatabase | 只在admin数据库中可用,赋予用户所有数据库的userAdmin权限 |
root | 只在admin数据库中可用。超级账号,超级权限 |
角色分类如下:
角色分类 | 角色罗列 |
数据库用户角色 | read、readWrite |
数据库管理角色 | dbAdmin、dbOwner、userAdmin |
集群管理角色 | clusterAdmin、clusterManager、clusterMonitor、hostManager |
备份恢复角色 | backup、restore |
所有数据库角色 | readAnyDatabase、readWriteAnyDatabase、userAdminAnyDatabase、dbAdminAnyDatabase |
超级用户角色 | root |
4.4、退出后用密码登录
java docker exec -it 542d12efed26 mongo -u wshanshi -p wshanshi --authenticationDatabase admin
4.5、切换用户
java db.auth("root","admin")
4.6、增删改查
具体可参考:MongoDB 操作
五、SpringBoot集成MongoDB
5.1、maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
5.2、application配置
spring:
data:
mongodb:
host: 127.0.0.1
port: 27017
username: wshanshi
password: wshanshi
database: admin
#authentication-database: root
connections-per-host: 20
min-connections-per-host: 20
5.3、config配置类
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import java.util.List;
@Component
@Validated
@Getter
@Setter
public class MongoSettingsProperties {
@Value("${spring.data.mongodb.database}")
private String database;
@Value("${spring.data.mongodb.host}")
private String host;
@Value("${spring.data.mongodb.port}")
private String port;
@Value("${spring.data.mongodb.username}")
private String username;
@Value("${spring.data.mongodb.password}")
private String password;
private String replicaSet;
private String authenticationDatabase;
private Integer minConnectionsPerHost = 10;
private Integer connectionsPerHost = 2;
}
六、Java操作示例
6.1、创建集合
public Object createCollection() {
// 设置集合名称
String collectionName = "users";
// 创建集合并返回集合信息
mongoTemplate.createCollection(collectionName);
// 检测新的集合是否存在,返回创建结果
return mongoTemplate.collectionExists(collectionName) ? "创建集合成功" : "创建集合失败";
}
6.2、查询集合
/**
* 获取【集合名称】列表
*
* @return 集合名称列表
*/
public Object getCollectionNames() {
// 执行获取集合名称列表
return mongoTemplate.getCollectionNames();
}
6.3、删除集合
/**
* 删除【集合】
*
* @return 创建集合结果
*/
public Object dropCollection() {
// 设置集合名称
String collectionName = "users1";
// 执行删除集合
mongoTemplate.getCollection(collectionName).drop();
// 检测新的集合是否存在,返回删除结果
return !mongoTemplate.collectionExists(collectionName) ? "删除集合成功" : "删除集合失败";
}
6.4、创建文档
/**
* 插入【一条】文档数据,如果文档信息已经【存在就抛出异常】
*
* @return 插入的文档信息
*/
public Object insert() {
// 设置用户信息
User user = new User()
// .setId("10")
.setAge(22)
.setSex("男")
.setRemake("无")
.setSalary(2800)
.setName("张三")
.setBirthday(new Date())
.setStatus(new Status().setHeight(180).setWeight(150));
// 插入一条用户数据,如果文档信息已经存在就抛出异常
User newUser = mongoTemplate.insert(user, COLLECTION_NAME);
// 输出存储结果
log.info("存储的用户信息为:{}", newUser);
return newUser;
}
/**
* 插入【多条】文档数据,如果文档信息已经【存在就抛出异常】
*
* @return 插入的多个文档信息
*/
public Object insertMany() {
// 设置两个用户信息
User user1 = new User()
// .setId("11")
.setAge(22)
.setSex("男")
.setRemake("无")
.setSalary(1500)
.setName("王五")
.setBirthday(new Date())
.setStatus(new Status().setHeight(180).setWeight(150));
User user2 = new User()
// .setId("12")
.setAge(22)
.setSex("男")
.setRemake("无")
.setSalary(1500)
.setName("赵柳")
.setBirthday(new Date())
.setStatus(new Status().setHeight(180).setWeight(150));
// 使用户信息加入结合
List<User> userList = new ArrayList<>();
userList.add(user1);
userList.add(user2);
// 插入一条用户数据,如果某个文档信息已经存在就抛出异常
Collection<User> newUserList = mongoTemplate.insert(userList, COLLECTION_NAME);
// 输出存储结果
for (User user : newUserList) {
log.info("存储的用户信息为:{}", user);
}
return newUserList;
}
如下图所示:
6.5、存储文档
/**
* 存储【一条】用户信息,如果文档信息已经【存在就执行更新】
*
* @return 存储的文档信息
*/
public Object save() {
// 设置用户信息
User user = new User()
// .setId("11")
.setAge(37)
.setSex("女")
.setRemake("无")
.setSalary(2800)
.setName("测试")
.setBirthday(new Date())
.setStatus(new Status().setHeight(169).setWeight(150));
// 存储用户信息,如果文档信息已经存在就执行更新
User newUser = mongoTemplate.save(user, COLLECTION_NAME);
// 输出存储结果
log.info("存储的用户信息为:{}", newUser);
return newUser;
}
6.6、查询全部文档
/**
* 查询集合中的【全部】文档数据
*
* @return 全部文档列表
*/
public Object findAll() {
// 执行查询集合中全部文档信息
List<User> documentList = mongoTemplate.findAll(User.class, COLLECTION_NAME);
// 输出结果
for (User user : documentList) {
log.info("用户信息:{}", user);
}
return documentList;
}
6.7、根据编号查询文档
/**
* 根据【文档ID】查询集合中文档数据
*
* @return 文档信息
*/
public Object findById(String id) {
// 根据文档ID查询集合中文档数据,并转换为对应 Java 对象
User user = mongoTemplate.findById(id, User.class, COLLECTION_NAME);
// 输出结果
log.info("用户信息:{}", user);
return user;
}
注意:在 MongoDB 中可以手动指定文档主键 ID,如果未手动指定则 MongoDB 会生成 12 位的 ObjectID。
若主键列设置为String类型,且未手动设置主键。后期根据id查询时会返回null。可根据具体业务场景设置合理的取值。
ObjectId类型数据如下图所示:
查询出来如下图所示:
6.8、分页查询、排序
/**
* 根据【条件】查询集合中【符合条件】的文档,获取其【文档列表】【分页】并【排序】
*
* @return 符合条件的文档列表
*/
public Object findByConditionAndSort(String sex, String sort, Integer page, Integer size) {
// 创建条件对象
Criteria criteria = Criteria.where("sex").is(sex);
// 创建查询对象,然后将条件对象添加到其中,然后根据指定字段进行排序
Query query = new Query(criteria).with(Sort.by(sort));
query.with(Sort.by(
// Sort.Order.asc("readOrNot"),
Sort.Order.desc("age")
));
// 执行查询
Pageable pageable = PageRequest.of(page, size);
// 查询记录总数
long totalCount = mongoTemplate.count(query, COLLECTION_NAME);
//查询分页后的记录
List<User> documentList = mongoTemplate.find(query.with(pageable), User.class, COLLECTION_NAME);
// 计算总页数
int totalPage = (int) (totalCount % size == 0 ? totalCount / size : totalCount / size + 1);
log.info("totalPage:{}", totalPage);
// 输出结果
for (User user : documentList) {
log.info("用户信息:{}", user);
}
return documentList;
}
6.9、聚合分组(group+count)
/**
* 使用管道操作符 $group 结合 $count 方法进行聚合统计
*
* @return 聚合结果
*/
public Object aggregationGroupCount() {
// 使用管道操作符 $group 进行分组,然后统计各个组的文档数量
AggregationOperation group = Aggregation.group("age").count().as("numCount");
// 将操作加入到聚合对象中
Aggregation aggregation = Aggregation.newAggregation(group);
// 执行聚合查询
AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, COLLECTION_NAME, Map.class);
for (Map result : results.getMappedResults()) {
log.info("{}", result);
}
return results.getMappedResults();
}
响应形式:
[
{
"_id": 34,
"numCount": 1
},
{
"_id": 55,
"numCount": 1
},
{
"_id": 22,
"numCount": 2
}
]
6.10、聚合分组(group+push)
/**
* 使用管道操作符 $group 结合表达式操作符 $push 获取某字段列表
*
* @return 聚合结果
*/
public Object aggregationGroupPush() {
// 先对数据进行排序,然后使用管道操作符 $group 进行分组,然后以数组形式列出某字段的全部值
AggregationOperation push = Aggregation.group("age").push("salary").as("salaryFirst");
// 将操作加入到聚合对象中
Aggregation aggregation = Aggregation.newAggregation(push);
// 执行聚合查询
AggregationResults<Map> results = mongoTemplate.aggregate(aggregation, COLLECTION_NAME, Map.class);
for (Map result : results.getMappedResults()) {
log.info("{}", result);
}
return results.getMappedResults();
}
响应形式:
[
{
"_id": 55,
"salaryFirst": [
1500
]
},
{
"_id": 22,
"salaryFirst": [
1500,
1500
]
},
{
"_id": 34,
"salaryFirst": [
1500
]
}
]
6.11、创建索引
/**
* 创建升序索引
*
* @return 索引信息
*/
public Object createAscendingIndex() {
// 设置字段名称
String field = "age";
// 创建索引
return mongoTemplate.getCollection(COLLECTION_NAME).createIndex(Indexes.ascending(field));
}
/**
* 创建降序索引
*
* @return 索引信息
*/
public Object createDescendingIndex() {
// 设置字段名称
String field = "age";
// 创建索引
return mongoTemplate.getCollection(COLLECTION_NAME).createIndex(Indexes.descending(field));
}
/**
* 创建升序复合索引
*
* @return 索引信息
*/
public Object createCompositeIndex() {
// 设置字段名称
String field1 = "name";
String field2 = "age";
// 创建索引
return mongoTemplate.getCollection(COLLECTION_NAME).createIndex(Indexes.ascending(field1, field2));
}
/**
* 创建文字索引
*
* @return 索引信息
*/
public Object createTextIndex() {
// 设置字段名称
String field = "name";
// 创建索引
return mongoTemplate.getCollection(COLLECTION_NAME).createIndex(Indexes.text(field));
}
/**
* 创建哈希索引
*
* @return 索引信息
*/
public Object createHashIndex() {
// 设置字段名称
String field = "name";
// 创建索引
return mongoTemplate.getCollection(COLLECTION_NAME).createIndex(Indexes.hashed(field));
}
/**
* 创建升序唯一索引
*
* @return 索引信息
*/
public Object createUniqueIndex() {
// 设置字段名称
String indexName = "name";
// 配置索引选项
IndexOptions options = new IndexOptions();
// 设置为唯一索引
options.unique(true);
// 创建索引
return mongoTemplate.getCollection(COLLECTION_NAME).createIndex(Indexes.ascending(indexName), options);
}
/**
* 创建局部索引
*
* @return 索引信息
*/
public Object createPartialIndex() {
// 设置字段名称
String field = "name";
// 配置索引选项
IndexOptions options = new IndexOptions();
// 设置过滤条件
options.partialFilterExpression(Filters.exists("name", true));
// 创建索引
return mongoTemplate.getCollection(COLLECTION_NAME).createIndex(Indexes.ascending(field), options);
}
6.12、查询索引
/**
* 获取当前【集合】对应的【所有索引】的【名称列表】
*
* @return 当前【集合】所有【索引名称列表】
*/
public Object getIndexAll() {
// 获取集合中所有列表
ListIndexesIterable<Document> indexList = mongoTemplate.getCollection(COLLECTION_NAME).listIndexes();
// 创建字符串集合
List<Document> list = new ArrayList<>();
// 获取集合中全部索引信息
for (Document document : indexList) {
log.info("索引列表:{}",document);
list.add(document);
}
return list;
}
6.13、删除索引
/**
* 根据索引名称移除索引
*/
public void removeIndex() {
// 设置索引名称
String indexName = "age_-1";
// 删除集合中某个索引
mongoTemplate.getCollection(COLLECTION_NAME).dropIndex(indexName);
}
/**
* 移除全部索引
*/
public void removeIndexAll() {
// 删除集合中全部索引
mongoTemplate.getCollection(COLLECTION_NAME).dropIndexes();
}
七、注意事项(踩坑)
7.1、ObjectId主键
说明:ObjectId 是一个12字节 BSON 类型数据,有以下格式:前4个字节表示时间戳,接下来的3个字节是机器标识码,紧接的两个字节由进程id组成(PID),最后三个字节是随机数。
MongoDB中存储的文档必须有一个"_id"键。这个键的值可以是任何类型的,默认是个ObjectId对象。
在一个集合里面,每个文档都有唯一的"_id"值,来确保集合里面每个文档都能被唯一标识。
MongoDB采用ObjectId,而不是其他比较常规的做法(比如自动增加的主键)的主要原因,因为在多个 服务器上同步自动增加主键值既费力还费时。
八、参考文档
8.1、Docker安装Mongodb
https://www.runoob.com/docker/docker-install-mongodb.html
8.2、Sheel命令操作文档
https://www.runoob.com/mongodb/mongodb-delete-collection.html
8.3、Java示例参考
http://www.mydlq.club/article/85/#1maven-%E5%BC%95%E5%85%A5%E7%9B%B8%E5%85%B3%E4%BE%9D%E8%B5%96
8.4、Github示例参考
https://github.com/my-dlq/blog-example/tree/master/springboot/springboot-mongodb-example