MongoDB入门手册

1. MongoDB介绍


开发中常见策略:把持久化数据存到RDBMS(如 MySQL、PostgreSQL …),把时效性很强的数据存到NoSQL(如Redis、MongoDB…)。

image-20240527153927622

MongoDB是一个开源分布式的文档型数据库,用于大量数据存储。它底层主要基于C++编写,首次发布于2009年。

MongoDB属于NoSQL家族的一员,它是所有NoSQL里,和关系型数据库最相似的。

MongoDB中的存储结构非常松散,数据格式是 BSON(Binary JSON,二进制JSON,和 JSON 一样支持内嵌的文档对象和数组对象),每个BSON文档都可以存储不同的结构。

MongoDB最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系型数据库单表查询的绝大部分功能,而且还支持对数据建立索引。

  • 官网地址:https://www.mongodb.com/
  • 文档地址:https://docs.mongoing.com/
  • 中文社区:https://www.mongodb.org.cn/
  • 仓库地址:https://github.com/mongodb/mongo

MongoDB的特点有哪些:

  • 吃得多:适合海量数据存储。(天生就是为了分布式而设计的,非常适合搭建集群存储数据)
  • 干活快:读写的性能非常强
  • 不挑食:可以存储非结构化的、松散的数据(什么样的数据都能存)

MongoDB的使用场景:

MongoDB适合什么场景:

  • 存储web网站数据
  • 作为缓存使用
  • 存储大尺寸、低价值的数据
  • 高伸缩性的场景,比如 路线图(mongo的路线图已经包含了对MapReduce引擎的支持)
  • 业务变化频繁的场景,比如 频繁的修改字段、增加字段等场景,使用MongoDB的结构松散的特性,可以很方便的适应需求。
  • 用于对象及JSON 数据的存储,Mongo 的BSON 数据格式非常适合文档化格式的存储及查询。比如 行动轨迹的存储(示例场景:快递物流的信息)

不适合什么场景:

  • 对事务要求比较高的场景
  • 不支持多表查询(跨集合的查询)
  • 不适合商业BI应用

2. MongoDB安装


基于Docker安装mongodb。

1、拉取镜像:

docker pull mongo:5.0.9

2、创建容器:

docker run -di --name=mongo \
  -e MONGO_INITDB_ROOT_USERNAME=admin123 \
  -e MONGO_INITDB_ROOT_PASSWORD=123 \
  -p 27017:27017 \
  -v /usr/local/src/mongo:/data/db \
  --restart=always \
  mongo:5.0.9
  • docker run: 创建并启动容器的命令。
  • --di:--detach 表示在后台运行容器, --init表示以交互模式运行容器。
  • --name=mongo: 设置容器名称。
  • -e MONGO_INITDB_ROOT_USERNAME=admin123 :设置账号
  • -e MONGO_INITDB_ROOT_PASSWORD=123:设置密码
  • -p 27017:27017: 端口映射
  • -v /usr/local/src/mongo:/data/db: 挂载数据卷。
  • --restart=always: 开机自启。

image-20240527021529344

3、使用DataGrip连接MongoDB:

image-20240527022251799

输入MongoDB数据库的ip地址、账号和密码,然后点击OK。

image-20240527023018275

image-20240527022721878

MongoDB常用客户端工具有:

  • MongoDB Shell
  • MongoDB Compass(官方提供的图形化界面)
  • Studio 3T(有免费版和收费版,之前免费版叫Robo 3T)
  • Navicat
  • IDEA、DataGrip(Database Tools and SQL)

3. MongoDB相关概念


MongoDB 主要由: 文档(document)、集合(collection)、数据库(database)这三部分组成的。

  • MongoDB 的文档(document),相当于关系数据库表中的一行记录。

  • 多个文档组成一个集合(collection),相当于关系数据库的表。

  • 多个集合(collection),逻辑上组织在一起,就是数据库(database)。

  • 一个 MongoDB 实例支持多个数据库(database)。


MySQL与MongoDB的对比:

MySQLMongoDB
database(数据库)database(数据库)
table(数据库表)collection(集合)
row(记录)document(文档)
column(字段)field(域)
index(索引)index(索引)
table joins(表连接)MongoDB不支持
primary key(主键)primary key(自定将_id字段设置为主键)


MongoDB支持不同数据类型:(和JS类型相似)

JSON: JavaScript Object Notation,JS对象标记法。

  • 整数,NumberInt(int类型32位4字节),NumberLong(long类型64位8字节)
  • 小数:Double
  • 字符串:String,使用单引号或双引号
  • 布尔:Boolean
  • 数组:Array
  • 时间戳:Timestamp
  • 嵌套json对象:Object
  • 对象ID:ObjectId,用于创建文档的ID
  • 空值:Null
  • 二进制数据:Binary Data,不建议把文件存入数据库,查询效率比较低

小结:

MongoDB的结构:一个数据库里可以有多个集合,一个集合里可以有多个文档

  • Database:数据库
  • Collection:集合,相当于一张表,可以理解为是一个List<Object>
  • Document:文档,相当于表里的记录,可以理解为List<Object>里的一个Object对应的json ;
  • Field:字段
  • Index:索引,提升查询数据的速度,但是也影响增删改的速度。

4. MongoDB操作


4.1 操作库和集合

# 查看所有库
show dbs   或则 show databases

# 切换库,如果不存在,会自动创建并切入
use 库名称

# 查看当前正在使用的库
db

# 删除当前库
db.dropDatabase() 或则show tables

#查看当前库的所有集合(表)
show collections

#删除集合
db.集合名称.drop()

注:并不需要提前创建集合,往集合里插入文档数据时,如果集合不存在,会自动创建。


示例1:查询有哪些库

show dbs

image-20240527141825809


示例2:切换库 或 创建该库

//创建成功之后,并不会显示,因为里面没有数据
use kunkun

image-20240527142501398


示例3:查询当前所在库

db

image-20240527142639533


示例4:创建集合

方式1:手动创建集合

db.createCollection("product")
  • db:表示当前库,他这里是面向对象思想的,对象.方法
  • createCollection(name,option):创建集合的方法
    • name: 要创建集合的名称(对应RDB的表)
    • options: 可选参数, 指定有关内存大小及索引的选项

image-20240527143719321


方式2:自动创建集合,当集合不存在时,往集合里插入文档数据会自动创建

// db.集合名.insertOne(json对象);
db.users.insertOne({name:"张三",age:18,gender:"男"});

image-20240527144305965


示例5:查询所有集合

show collections  

image-20240527145345249


示例6:删除product集合

db.product.drop();

image-20240527145639975


4.2 操作文档

文档地址:https://docs.mongoing.com/mongodb-crud-operations

# 插入一条文档
db.集合名.insertOne(json对象)

# 批量插入文档
db.集合名.insertMany([json对象,json对象,...])

# 修改一条文档
db.集合名.updateOne(query,update)

# 批量修改文档
db.集合名.updateMany(query,update)

# 删除一条文档
db.集合名.deleteOne(query)

# 批量删除文档
db.集合名.deleteMany(query)

示例1:新增文档

// 新增文档
db.users.insertOne({"name": "李四","age": 20,"gender": "女"});
// 批量新增
db.users.insertMany([
    {"name": "王五","age": 30,"gender": "女"},
    {"name": "赵六","age": 28,"gender": "男"}
]);

image-20240527152747457

//扩展:js语法,提前声明一个json数组,再批量新增
var arr = [
    {name:"齐天泽1", age:23, sex:"男"},
    {name:"吴磊1", age:25, sex:"男"}
];
db.users.insertMany(arr);

/*
// 循环插入
for(var i=0;i<arr.length;i++){
	db.users.insertOne(arr[i]);
}
*/

示例2:修改符合条件的文档

默认情况下update操作会对原始文档进行覆盖。如果不想覆盖,而是仅仅做增量更新的话,要使用相应的操作符:

  • $set:设置字段的值。

    例如db.集合名.updateMany({}, { $set:{money:1000, status: 1} }),表示修改集合里所有的数据,把money设置为1000,把status设置为1;

  • $inc:字段的值自增

    例如db.集合名.updateMany({}, {$inc: {money:1000} }),表示修改集合里所有的数据,给money值自增1000;

//修改一条文档,修改男同学的年龄和薪资
// update users set age=20,salary=10000 where gender='男'
db.users.updateOne(
    {gender:"男"}, 
    {$set:{age:20,salary:10000} 
});

image-20240527160137931

//批量修改:修改女同学的年龄和薪资
db.users.updateMany(
    {gender:"女"}, 
    {$set:{age:20,salary:10000} 
});

//批量修改:每个员工加5000
db.users.updateMany(
    {}, 
    {$inc:{salary:5000} 
});

image-20240527160352354


示例3:删除符合条件的文档

//删除一条文档
db.users.deleteOne({gender:"男"})

// 批量删除文档
db.users.deleteMany({gender:"女"})

// 删除所有文档
db.users.deleteMany({})

4.3 查询文档

语法:

db.集合名.find(query,fields).sort(排序条件).skip(m).limit(n)
  • query:表示查询条件,写成json形式,例如 {status:1}表示 查询status=1的文档。如果需要其它条件,参考下一小节
  • fields:表示要查询哪些字段,写成json形式,例如 {name:1, age:1} 只查询name和age字段
  • 排序条件:写成json形式,例如{age:1, money:-1} 表示 按age升序排序,如果年龄相同则按money降序
  • skip(m):表示跳过前m个,从索引m开始查找
  • limit(n):表示只要前n个

查询条件操作符

操作语法示例对比SQL语句
=db.col.find({age:20})age=20
<>db.col.find({age:{$ne:20}})age<>20
<db.col.find({age:{$lt:20}})age<20
<=db.col.find({age:{$lte:20}})age<=20
>db.col.find({age:{$gt:20}})age>20
>=db.col.find({age:{$gte:20}})age>=20
indb.col.find({age:{$in:[21,23]}})age in(21,23)
not indb.col.find({age:{$nin:[21,23]}})age not in(21,23)
likedb.col.find({username:{$regex:‘正则表达式’}})username like ‘’

多条件连接符:

操作语法示例RDBMS中的类似语句
ordb.col.find({$or:[{sex:"女"},{age:22}]})sex='女' or age=22
anddb.col.find({sex:"女",age:22})sex='女' or age=22

示例:

数据准备:

db.users.remove({}) 
db.users.insert({username:'zhangsan', sex:'男', age:20, salary:8000})
db.users.insert({username:'lisi', sex:'女',age:21, salary:9000})
db.users.insert({username:'wangwu', sex:'男',age:22, salary:12000})
db.users.insert({username:'zhaoliu', sex:'男',age:22, salary:9500})
db.users.insert({username:'qianqi', sex:'女',age:25, salary:7500})
db.users.insert({username:'tom', sex:'男',age:19, salary:6500})
db.users.insert({username:'jerry', sex:'女',age:23, salary:8500})
db.users.insert({username:'jack', sex:'男',age:22, salary:4500})
db.users.insert({username:'rose', sex:'女',age:20, salary:14500})

测试代码:

// 查所有文档
db.users.find()
db.users.find({})
select * from users //兼容部分sql预计

//1. 单字段的条件查询
//  等值查询性别为男的 select * from users where sex = '男'
db.users.find( { sex:"男"} )
//  > 查询年龄大于20岁的 select * from users where age > 20
db.users.find( { age:{$gt:20} } )
db.users.find( { age:{$gt:20, $lt:23} } )
//  like 查询username以j开头的 select * from users where username like 'j%'
db.users.find( { username:{$regex:'^j.*$'} } ) //$regex:正则

// 查询符合条件的数据,只要name和salary列
db.users.find( {},{username:1, salary:1} )

//2. 多字段的条件查询
// 查询工资8000以上,年龄23岁以下的用户:salary,age
db.users.find( { salary:{$gt:8000}, age:{$lt:23} } )
// 查询工资8000以上,或者 年龄23岁以下的用户:salary,age
db.users.find( { $or:[ {salary:{$gt:8000}},{age:{$lt:23}} ] } )

//3. 排序
//sort()函数用于对查询结果进行排序,1 是升序,-1 是降序
db.users.find().sort({ salary:-1 })
db.users.find().sort({age:1})
//按年龄升序,如果相同年龄再按薪资降序
db.users.find().sort({age:1, salary:-1})

//4. 分页
db.users.find().skip(3) //跳过前三个
db.users.find().skip(3).limit(2)//跳过前三个,只要2个

小节:

整体的语法:
db.集合名.find( query,fields ).sort({排序字段:1-1, 排序字段:1-1}).skip(起始索引).limit(查询数量)
db.users.find().sort({age:-1, salary:1}).skip(5).limit(5)

查询条件:
	* 以字段为单位 写条件的 { 字段1:{}, 字段2:{} }
	* 条件运算符:$gt, $gte, $lt, $lte, $ne, $in, $nin, $regex等等

4.4 MongoDB索引

MySQL的索引底层使用B+树,B+树的特点:所有的数据都在叶子节点上,它的非叶子节点只存储索引值和下级节点的地址。这样做的好处,每个节点都可以有更多的下级结点,树的层级就会更浅。

MongoDB的索引底层使用B树,B树的特点:无论是叶子结点还是非叶子节点他们都存储了索引值和数据。

mongoDB中常用的索引类型:

  • 单字段索引:在单字段上创建索引*
  • 复合索引:在多字段上创建索引*
  • 多键索引:在数组字段上创建索引
  • 哈希索引:给指定字段建立hash索引,有强大的查找能力,但是不能排序及范围查询

mongoDB中常用的索引属性:

  • 唯一索引:添加唯一性约束
  • 局部索引:只对集合里符合条件的一部分文档创建索引
  • 稀疏索引:在有索引字段的文档上创建索引。例如给email字段加稀疏索引,只有文档里包含email字段才会加索引
  • TTL索引:一种特殊的单字段索引,只能用于date类型的字段,给文档添加过期时间,过期后文档会被删除掉

操作索引语法:

语法说明
db.集合名.getIndexes()查询指定集合的索引
db.集合名.createIndex({索引定义},{索引选项})给指定集合创建索引
db.集合名.find(…).explain(是否显示详细信息)查看执行计划,类似于MySQL里的explain

示例代码:

//查询集合里的索引
db.users.getIndexes();

image-20240527201619662

//添加索引:多字段索引
//说明:1表示升序创建索引,-1表示降序创建索引。
db.users.createIndex( { username:1, age:1 } )
//查询集合里的索引
db.users.getIndexes();

image-20240527202231369

//查询数据时,验证索引是否会生效
db.users.find( {username:"tom"} ).explain(true);

/*
winningPlan:最佳执行计划
"stage" : "FETCH", //查询方式,常见的有COLLSCAN全表扫描、IXSCAN索引扫描、FETCH根据索引去检索文档、SHARD_MERGE合并分片结果、IDHACK针对_id进行查询
*/

image-20240527202657564

//删除索引
db.users.dropIndex("username_1_age_1")

image-20240527204117904

image-20240527204200631


5. Java操作MongoDB


SpringBoot提供了操作MongoDB的起步依赖:spring-boot-starter-data-mongodb,已经帮我们提供了操作MongoDB的一些API,包括两种形式:

  • JPA形式的操作:实现了JPA规范的操作方式,简单的CURD操作更方便,但是复杂操作不够灵活

  • MongoTemplate形式的操作:更灵活的操作MongoDB的方式(就老生常谈了,SpringData家族的共性套路)

5.1 快速入门

1、创建一个springboot工程

2、添加mongodb相关依赖

<!-- MongoDB起步依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

<!-- 为方便测试,再添加单元测试起步依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
</dependency>

3、配置mongodb连接参数

spring:
  data:
    mongodb:
      #uri的格式:  mongodb://帐号:密码@ip:端口/库名称?authSource=admin
      uri: mongodb://admin123:123@192.168.150.101:27017/kunkun?authSource=admin

4、准备实体类

package cn.aopmin.po;

import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Data
@Document("course")//与mongodb中的集合进行绑定
public class Course {
    @Id//主键
    private String id;
    private String name;
    private Integer price;
    private Integer status;
}

5、新增一条文档

  • 注入MongoTemplate对象
  • 往集合里插入数据:调用insert或save方法
@SpringBootTest
public class MongoTest {
    @Autowired
    private MongoTemplate mongoTemplate;

    @Test
    public void testInsert(){
        Course course = new Course();
        course.setName("Java基础");
        course.setPrice(1);
        course.setStatus(1);
		
        mongoTemplate.insert(course);
    }
}

5.2 MongoTemplate常用API

方法名说明
insert(object对象)插入一条文档数据
insertAll(集合)批量插入文档数据
updateFirst(query,update,class)修改一条文档数据
updateMulti(query,update,class)批量修改文档数据
save(object对象)保存一条文档数据(有就改,无就增)
remove(query,class)删除符合条件的文档数据
remove(object对象)删除指定文档数据
findById(id值,class)根据id查询文档
findOne(query,class)根据条件查询一条文档
find(query,class)根据条件查询文档
count(query,class)统计文档的数量

注意其中的query和update:

  • query:用于设置查询条件,类似mysql中的where条件
  • update:用于设置要修改的数据,类似mysql中set子句

示例代码:

@SpringBootTest
public class MongoTest {
    @Autowired
    private MongoTemplate mongoTemplate;

    @Test
    public void testInsert() {
        Course course = new Course();
        course.setName("Java基础");
        course.setPrice(1);

        mongoTemplate.insert(course);
    }

    @Test
    public void testInsertAll() {
        List<Course> list = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            Course course = new Course();
            course.setName("Java基础" + i);
            course.setPrice(100 + i);

            list.add(course);
        }
        mongoTemplate.insertAll(list);
    }
    

    @Test
    public void testUpdateFirst() {
        Query query = Query.query(Criteria.where("name").is("Java基础"));
        Update update = Update.update("status", 0);
        mongoTemplate.updateFirst(query, update, Course.class);
    }

    @Test
    public void testUpdateMulti() {
        Query query = Query.query(Criteria.where("price").gt(150));
        Update update = Update.update("status", 0);
        mongoTemplate.updateMulti(query, update, Course.class);
    }

    @Test
    public void testRemove() {
        Query query = Query.query(Criteria.where("name").is("Java基础"));
        mongoTemplate.remove(query, Course.class);
    }

    @Test
    public void testFindById() {
         Course course = mongoTemplate.findById("6588372d561ff41f2377fc4e", Course.class);
         System.out.println("course = " + course);
    }

    @Test
    public void testFindOne() {
        Query query = Query.query(Criteria.where("name").is("Java基础1"));
        Course course = mongoTemplate.findOne(query, Course.class);
        System.out.println("course = " + course);
    }

    @Test
    public void testFindAndCount() {
        //1. 统计符合条件的文档数量
        Query query = Query
                //设置查询条件:where price > 120 and status is null
                .query(Criteria.where("price").gt(120).and("status").is(null));
        long count = mongoTemplate.count(query, Course.class);
        System.out.println("count = " + count);

        //2. 分页查询符合条件的文档列表
        query
                //设置排序条件:order by name desc
                .with(Sort.by(Sort.Order.desc("name")))
                //设置分页条件:查询第1页的10条。 注意:这里的页码从0开始
                .with(PageRequest.of(0, 10));
        List<Course> list = mongoTemplate.find(query, Course.class);
        list.forEach(System.out::println);
    }
}

小结:

准备工作:
	1. 导入依赖spring-boot-starter-data-mongodb
	2. 修改配置文件,设置MongoDB的地址
		spring.data.mongodb.uri = 地址
		地址写法: mongodb://帐号:密码@ip:端口/库名称?authSource=admin
	3. 注入MongoTemplate对象
准备实体类:对应MongoDB的集合
	类上要加注解 @Document(collection="集合名")
	类上可以设置索引:@CompoundIndex(def="{username:1, age:-1}",name="索引名")
	类里主键字段:要加注解@Id,类型可以是ObjectId,也可以是String
使用MongoTemplate操作MongoDB:
	查询条件对象:Query
		1. Query
			.query(Criteria.where("字段名").is().and("字段名").gt()...)
			.with(Sort.by(Sort.Order.desc("排序字段"), Sort.Order.asc("排序字段")))
			.with(PageRequest.of(页码, 每页几条)); //注意页码从0开始
		2. new Query()
			.addCriteria(Criteria.where("字段名").is().and("字段名").gt()...)
			.with(Sort.by(Sort.Order.desc("排序字段"), Sort.Order.asc("排序字段")))
			.with(PageRequest.of(页码, 每页几条)); //注意页码从0开始
	修改条件对象:Update
		1. Update.update("字段名",).set("字段名",)...inc("字段名",)
		2. new Update().set("字段名",)...inc("字段名",)
	常用方法:
		mongoTemplate.insert(对象)
		mongoTemplate.insertAll(集合)
		mongoTemplate.updateFirst(query, update, Class clazz)
		mongoTemplate.updateMulti(query, update, Class clazz)
		mongoTemplate.remove(query, Class clazz)
		mongoTemplate.findById(id值, Class clazz)
		mongoTemplate.findOne(query, Class clazz)
		mongoTemplate.find(query, Class clazz)
		mongoTemplate.count(query, Class clazz)
  • 18
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

白豆五

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值