人生哪能多如意,万事只求半称心。
MongoDB 官网:https://www.mongodb.com
MongoDB 中文社区:https://www.mongodb.org.cn
MongoDB是一个开源、高性能、支持海量数据存储的文档型数据库(也是nosql大家庭的一员),它是C++编写的,问世于2000年代中期。
MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,数据格式是 BSON,一种类似 JSON 的二进制形式的存储格式,简称 Binary JSON,和 JSON 一样支持内嵌的文档对象和数组对象,因此可以存储比较复杂的数据类型。
MongoDB最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。
什么类型的数据适合存储MongoDB中?
MongoDB特点:
是一个开源的、高性能的文档型数据库,属于NoSQL的一种;
数据存储方面:内存+磁盘;
高扩展方面:内置数据分片,方便水平扩展;
MongoDB与Redis和MySQL对比:
MongoDB与Redis对比:
MongoDB与MySQL对比:
三者查询效率对比:
MongoDB适用场景:
MongoDB体系结构与术语:
MongoDB 主要由: 文档(document)、集合(collection)、数据库(database)这三部分组成的。
MongoDB与关系型数据库(RDBMS)术语对比:
SQL术语/概念 | MongoDB术语/概念 | 解释/说明 |
---|---|---|
database | database | 数据库 |
table | collection | 数据库表/集合 |
row | document | 表中的一条数据 |
column | field | 数据字段/域 |
index | index | 索引 |
table joins | 表连接,MongoDB不支持 | |
primary key | primary key | 主键,MongoDB自动将 _id 字段设置为主键 |
如果不指定id,mongodb会自动生成一个id。
关系型数据库和MongoDB数据对应关系:
MongoDB数据类型:
基于Docker部署MongoDB环境。
1、下载镜像
docker pull mongo:4.2.1
2、启动容器
docker run -di --name mongo-service --restart=always -p 27017:27017 -v /docker/mongodata:/data mongo:4.2.1
3、使用客户端工具连接mongodb(默认端口号:27017)
本文以Robo3T工具为例,使用教程如下:
通过命令操作mongodb。
#查看所有的数据库
> show dbs
#通过use关键字切换数据库
> use admin
#创建数据库
#说明:在MongoDB中,数据库是自动创建的,通过use切换到新数据库中,进行插入数据即可自动创建数据库
> use testdb
> show dbs #并没有创建数据库
> db.user.insert({id:1,name:'zhangsan'}) #插入数据
> show dbs
#查看表
> show tables
> show collections
#删除集合(表)
> db.user.drop()
true #如果成功删除选定集合,则 drop() 方法返回 true,否则返回 false。
#删除数据库
> use testdb #先切换到要删除的数据中
> db.dropDatabase() #删除数据库
在MongoDB中,存储的文档结构是一种类似于json的结构,称之为bson(全称为:Binary JSON)。
#插入数据
#语法:db.表名.insert(json字符串)
> db.user.insert({id:1,username:'zhangsan',age:20})
> db.user.find() #查询数据
update() 方法用于更新已存在的文档。语法格式如下:
db.collection.update(
<query>,
<update>,
[
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>
]
)
参数说明:
#查询全部
> db.user.find()
#更新数据
> db.user.update({id:1},{$set:{age:22}})
#注意:如果这样写,会删除掉其他的字段
> db.user.update({id:1},{age:25})
#更新不存在的字段,会新增字段
> db.user.update({id:2},{$set:{sex:1}}) #更新数据
#更新不存在的数据,默认不会新增数据
> db.user.update({id:3},{$set:{sex:1}})
#如果设置第一个参数为true,就是新增数据
> db.user.update({id:3},{$set:{sex:1}},true)
通过remove()方法进行删除数据,语法如下:
db.collection.remove(
<query>,
{
justOne: <boolean>,
writeConcern: <document>
}
)
参数说明:
示例:
#删除数据
> db.user.remove({})
#插入4条测试数据
db.user.insert({id:1,username:'zhangsan',age:20})
db.user.insert({id:2,username:'lisi',age:21})
db.user.insert({id:3,username:'wangwu',age:22})
db.user.insert({id:4,username:'zhaoliu',age:22})
> db.user.remove({age:22},true)
#删除所有数据
> db.user.remove({})
MongoDB 查询数据的语法格式如下:
db.user.find([query],[fields])
条件查询:
操作 | 格式 | 范例 | RDBMS中的类似语句 |
---|---|---|---|
等于 | { } |
db.col.find({"by":"Java之父ysj"}).pretty() |
where by = 'Java之父ysj' |
小于 | { |
db.col.find({"likes":{$lt:50}}).pretty() |
where likes < 50 |
小于或等于 | { |
db.col.find({"likes":{$lte:50}}).pretty() |
where likes <= 50 |
大于 | { |
db.col.find({"likes":{$gt:50}}).pretty() |
where likes > 50 |
大于或等于 | { |
db.col.find({"likes":{$gte:50}}).pretty() |
where likes >= 50 |
不等于 | { |
db.col.find({"likes":{$ne:50}}).pretty() |
where likes != 50 |
实例:
#插入测试数据
db.user.insert({id:1,username:'zhangsan',age:20})
db.user.insert({id:2,username:'lisi',age:21})
db.user.insert({id:3,username:'wangwu',age:22})
db.user.insert({id:4,username:'zhaoliu',age:22})
db.user.find() #查询全部数据
db.user.find({},{id:1,username:1}) #只查询id与username字段
db.user.find().count() #查询数据条数
db.user.find({id:1}) #查询id为1的数据
db.user.find({age:{$lte:21}}) #查询小于等于21的数据
db.user.find({$or:[{id:1},{id:2}]}) #查询id=1 or id=2
#分页查询:Skip()跳过几条,limit()查询条数
db.user.find().limit(2).skip(1) #跳过1条数据,查询2条数据
db.user.find().sort({id:-1}) #按照id倒序排序,-1为倒序,1为正序
索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。
这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。
索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构
#创建索引
> db.user.createIndex({'age':1})
#查看索引
> db.user.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "testdb.user"
}
]
#说明:1表示升序创建索引,-1表示降序创建索引。
MongoDB 查询分析可以确保我们建议的索引是否有效,是查询语句性能分析的重要工具。
#插入1000条数据
for(var i=1;i<1000;i++)db.user.insert({id:100+i,username:'name_'+i,age:10+i})
#查看执行计划
> db.user.find({age:{$gt:100},id:{$lt:200}}).explain()
#测试没有使用索引
> db.user.find({username:'zhangsan'}).explain()
#winningPlan:最佳执行计划
#"stage" : "FETCH", #查询方式,常见的有COLLSCAN/全表扫描、IXSCAN/索引扫描、FETCH/根据索引去检索文档、SHARD_MERGE/合并分片结果、IDHACK/针对_id进行查询
1、构建springboot项目
2、导入mongodb依赖
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-mongodbartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
dependencies>
3、向application.yml添加配置
server:
port: 9998
spring:
data:
mongodb:
host: 127.0.0.1
port: 27017
database: mongo-test
4、编写实体类
package com.example.mongodemo.pojo;
import lombok.Data;
import org.springframework.data.mongodb.core.mapping.Document;
/**
* 学生实体类
*
* @author 白豆五
* @version 2023/06/9
* @since JDK8
*/
@Data
@Document("student") //与mongodb集合进行绑定
public class Student {
private String id;
private String name;
private char sex;
private Integer age;
private String major;
private Double score;
}
新增方法有:insert、save()方法。
package com.example.mongodemo.test;
import com.example.mongodemo.pojo.Student;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.mongodb.core.MongoTemplate;
/**
* @author 白豆五
* @version 2023/06/9
* @since JDK8
*/
@SpringBootTest
public class MongoTest {
@Autowired
private MongoTemplate mongoTemplate;
/**
* 测试新增文档
*/
@Test
public void testAddDocument() {
for (int i = 0; i < 10; i++) {
Student student = new Student();
student.setId(String.valueOf(i));
student.setName("user" + i);
student.setSex((i % 2 == 0 ? '女' : '男'));
student.setAge(20);
student.setMajor("Java");
student.setScore(98.0);
// mongoTemplate.insert(student);//可以新增一条或多条数据
mongoTemplate.save(student); //表示新增或更新一条数据,不能操作多条
}
/*
insert和save方法的区别:
1.insert可以批量保存多条数据,save只能保存一条数据;
2.insert方法只能表示新增,而使用save方法在数据不存在的时候是新增,在数据存在时则是更新。
*/
}
}
查询方法有:findById、findOne、findAll、find()方法。
/**
* 测试查询文档
*/
@Test
public void testQueryDocument() {
// 1.查询一条数据,根据主键id查询
Student s1 = mongoTemplate.findById("1", Student.class);
System.out.println(s1); //Student(id=1, name=user1, sex=男, age=20, major=Java, score=98.0)
// 2.查询一条数据,根据指定域查询(字段) ,sql: select * from student where name='user1';
Student s2 = mongoTemplate.findOne(Query.query(
// 构造查询条件
Criteria.where("name").is("user3") //Criteria类提供了很多查询条件
), Student.class);
System.out.println(s2); // Student(id=3, name=user3, sex=男, age=20, major=Java, score=98.0)
// 3.查询列表,查询全部数据
List<Student> list = mongoTemplate.findAll(Student.class);
System.out.println(list.size());//10
// 4.查询列表,指定条件查询(查询所有年龄大于18岁的男生信息)
list = mongoTemplate.find(Query.query(
Criteria.where("sex").is('男').and("age").gt(18)
// is 等于查询
// gt le ...
// in 范围查询
// ne 不等于查询
// like 模糊查询
// 拼接条件用and方法
), Student.class);
list.forEach(s -> System.out.println(s));
/*
Student(id=1, name=user1, sex=男, age=20, major=Java, score=98.0)
Student(id=3, name=user3, sex=男, age=20, major=Java, score=98.0)
Student(id=5, name=user5, sex=男, age=20, major=Java, score=98.0)
Student(id=7, name=user7, sex=男, age=20, major=Java, score=98.0)
Student(id=9, name=user9, sex=男, age=20, major=Java, score=98.0)
*/
System.out.println("===========");
// 5.查询列表,根据id倒排序
list = mongoTemplate.find(Query.query(Criteria.where("sex").is('女')).with(Sort.by(Sort.Direction.DESC, "id")), Student.class);
list.forEach(s -> System.out.println(s));
/*
Student(id=8, name=user8, sex=女, age=20, major=Java, score=98.0)
Student(id=6, name=user6, sex=女, age=20, major=Java, score=98.0)
Student(id=4, name=user4, sex=女, age=20, major=Java, score=98.0)
Student(id=2, name=user2, sex=女, age=20, major=Java, score=98.0)
Student(id=0, name=user0, sex=女, age=20, major=Java, score=98.0)
*/
System.out.println("===========");
// 6.查询列表,只查前3条数据limit(3)
list = mongoTemplate.find(Query.query(Criteria.where("sex").is('女'))//指定查询条件
.with(Sort.by(Sort.Direction.DESC, "id")) //排序
.limit(3) //限制查询条数
, Student.class);
list.forEach(s -> System.out.println(s));
/*
Student(id=8, name=user8, sex=女, age=20, major=Java, score=98.0)
Student(id=6, name=user6, sex=女, age=20, major=Java, score=98.0)
Student(id=4, name=user4, sex=女, age=20, major=Java, score=98.0)
*/
}
更新方法有:save、update、findAndModify()方法。
/**
* 测试更新文档
*/
@Test
public void testUpdateDocument() {
// 方式1:使用save方法更新文档(根据id更新)
Student student = mongoTemplate.findById("1", Student.class);
if (student != null) {
student.setAge(18);
mongoTemplate.save(student);
}
// 方式2:根据指定条件更新文档(根据用户名更新)(非线程安全方法)
mongoTemplate.updateFirst(
Query.query(Criteria.where("name").is("user2")) //更新条件
, new Update().set("name", "小明") //更新的值
, Student.class //数据类型
);
// 方式2:根据指定条件更新文档(对小明的成绩+1)(非线程安全方法)
mongoTemplate.updateFirst(
Query.query(Criteria.where("name").is("小明")) //更新条件
, new Update().inc("score", 1) //inc 1自增, -1 自减
, Student.class //数据类型
);
// 方式3:指定更新条件(线程安全方法,保证操作数据的原子性)
mongoTemplate.findAndModify(
Query.query(Criteria.where("name").is("小明")) //更新条件
, new Update().inc("score", 1) //inc 1自增, -1 自减
, Student.class //数据类型
);
}
删除方法有:remove、update、findAndModify()方法。
/**
* 测试删除文档
*/
@Test
public void testDeleteDocument() {
// 删除方式1:根据ID删除
Student student = mongoTemplate.findById("1", Student.class);
if (student != null) {
mongoTemplate.remove(student);
}
// 删除方式2:指定条件删除(删除年龄大于18的学生信息)
mongoTemplate.remove(Query.query(Criteria.where("sex").gt(18)),Student.class);
}