前言

本篇主要简单介绍 noSql 概念、mongodb 的基础知识、docker-compose 方式搭建部署以及 springboot2.x 整合 mongodb 进行基础 CRUD

一、NoSQL 简介

NoSQL,指的是非关系型的数据库。NoSQL 有时也称作 Not Only SQL 的缩写,是对不同于传统的关系型数据库的数据库管理系统的统称。

NoSQL 用于超大规模数据的存储。(例如谷歌或 Facebook 每天为他们的用户收集万亿比特的数据。这些类型的数据存储不需要固定的模式,无需多余操作就可以横向扩展。

二、NoSQL VS RDBMS

RDBMS

NoSQL

高度组织化结构化数据

结构化查询语言(SQL)

数据和关系都存储在单独的表中。

数据操纵语言,数据定义语言

严格的一致性

基础事务

代表着不仅仅是 SQL

没有声明性查询语言

没有预定义的模式

- 值对存储,列存储,文档存储,图形数据库

最终一致性,而非 ACID 属性

非结构化和不可预知的数据

CAP 定理

高性能,高可用性和可伸缩性

三、CAP 定理(CAP theorem

在计算机科学中, CAP 定理(CAP theorem), 又被称作 布鲁尔定理

Brewer's theorem), 它指出对于一个分布式计算系统来说,不可能同时满足以下三点:

· 一致性(Consistency) (数据一致更新,所有数据变动都是同步的)

 

· 可用性(Availability) (保证每个请求不管成功或者失败都有响应,也就是好的响应性能)

 

· 分隔容忍(Partition tolerance) (系统中任意信息的丢失或失败不会影响系统的继续运作,可理解成高可用,一个节点崩了,并不影响我们其它

 

的节点)

CAP 理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,最多只能同时较好的满足两个。

因此,根据 CAP 原理将 NoSQL 数据库分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三大类:

· CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。

· CP - 满足一致性,分区容错性的系统,通常性能不是特别高。

· AP - 满足可用性,分区容错性的系统,通常可能对一致性要求低一些。

这里给你们分析一下,为什么 CAP 定理只能满足两个: 首先,看看它们分别的场景啊

1、满足 C,所有的机器上的数据都是一样,场景如下 : 每当一个新数据新增到其中一个服务器上,这个数据要同步到其它服务器,这样的情况下才可以保证 C

2、满足 A,场景如下: 用户随时都在访问,都能在可控的时间内返回正确的数据

3、满足 P,场景如下: 那必须是机器越多越可靠,为啥?我有 1 亿台服务器, 挂了几万台,完全没影响嘛。

然后,了解清楚了,现在分析一下为啥只能满足其中两个满足 C 和 A,那么 P 为啥不能满足

满足 C,是不是我们所有服务器的数据都要一致对吧,比如,你在其中一个节点服务器修改了某个人的 name 为小明,其它服务器也要跟着一起同步那个操作吧,那么你同步的时候,是不是需要时间的,然后机器越多,花的时间就越长,你想想,我们还要满足 A,也就是响应时间是不能太长,那么我们就要在很快的时间去完成同步,那这种情况下,机器就不能太多了,那么我集群节点少了, 那么分区容错性 P 就满足不了啦,是吧?

满足 C 和 P,那么 A 为啥不能满足

满足 C,跟上面说的一样,然后还要满足 P,那就是分区容错性要高,那么我的节点数就会多起来,要去耗时做同步操作,那么就不能在很快时间响应请求获取数据,那么 A 就满足不了了,是吧?你可以跟上面那条对比看看

满足 A 和 P,那么 C 为啥不能满足

满足 A,首先你是要满足请求数据响应时间很快,然后你还要满足 P,要有分区容错性,前面也说了,如果你要达到响应时间快,而且还要有符合 P,也就是会有多个集群节点,那么你就不没办法再去满足数据一致性,因为数据同步一致要时间,分区容错要多机器,那么满足 AP 的话,也许会造成各个节点响应的数据是不一样的,有的节点还是修改前,有点节点还是修改后。

四、NoSQL 的优点/缺点

优点:

· - 高可扩展性

· - 分布式计算

· - 低成本

· - 架构的灵活性,半结构化数据

· - 没有复杂的关系缺点:

· - 没有标准化,可能会对 bean 对象封装造成不便

· - 大多数 nosql 都不支持事务

· - 现有大部分产品不够成熟

 

五、BASE

BASE NoSQL 数据库通常对可用性及一致性的弱要求原则:

· Basically Availble --基本可用

· Soft-state --软状态/柔性事务。 "Soft state" 可以理解为"无连接",

"Hard state" "面向连接"

· Eventual Consistency --最终一致性 最终一致性, 也是是 ACID 的最终目的

 

六、ACID vs BASE

相对之下的,关系型数据库要遵循的则是 ACID 规则

ACID

BASE

原子性(Atomicity)

基本可用(Basically Available)

一致性(Consistency)

软状态/柔性事务(Soft state)

隔离性(Isolation)

最终一致性 (Eventual consistency)

持久性 (Durable)

 

七、NoSQL数据库分类

类型

部分代表

特点

列存储

 

Hbase Cassandra Hypertable

 

顾名思义,是按列存储数据的。最大的特点是方便存储结构化和半结构化数据,方便做数据压缩,对针对某一列或者某几列的查询有非常大的 IO

优势。

文档存储

MongoDB CouchDB

文档存储一般用类似 json 的格式存储,存储的内容是文档型的。这样也就有有机会对某些字段建立索引,实现关系数

据库的某些功能。

key-value 存储

TokyoCabinet/Tyrant BerkeleyDB

MemcacheDB

 

Redis

可以通过 key 快速查询到其value。一般来说,存储不管value 的格式,照单全收。

(Redis 包含了其他功能

 

图存储

 

Neo4J FlockDB

 

图形关系的最佳存储。使用传统关系数据库来解决的话性能低下,而且设计使用不方

便。

对象存储

db4o Versant

通过类似面向对象语言的语法操作数据库,通过对象的方

式存取数据。

xml 数据库

BerkeleyDBXML BaseX

高效的存储 XML 数据,并支持

XML 的内部查询语法,比如XQuery,Xpath。

 

八、MongoDB 是什么

1、简介

MongoDB 是用 C++语言编写的非关系型数据库。特点是高性能、易部署、易使用,存储数据十分方便。

1.1 、主要特性有

· 面向集合存储,易于存储对象类型的数据

· 模式自由

· 支持动态查询

· 支持完全索引,包含内部对象

· 支持复制和故障恢复

· 使用高效的二进制数据存储,包括大型对象

· 文件存储格式为 BSON(一种 JSON 的扩展)

 

对比项

MongoDB

MySQL Oracle

集合

二维表 table

表的一行数据

文档 document

一条记录 record

表字段

key

字段 field

字段值

value

value

主外键

PKFK

灵活度扩展性

极高

1.2 MongoDB 与关系型数据库对比

逻辑结构关系对比

关系型数据库:

MySQL 数据库(database)、表(table)、记录(rows)三个层次概念组成。非关系型数据库:

MongoDB 数据库(database)、集合(collection)、文档对象(document)三个层次概念组成。

MongoDB 里的集合对应于关系型数据库里的表,但是集合中没有列、行和关系的概念,集合中只有文档,一个文档就相当于一条记录,这体现了模式自由的特点。

1.3MongoDB 基本概念

文档(document) MongoDB 中数据的基本单元,非常类似于关系型数据库系统中的行(但是比行要复杂的多);

集合(collection)就是一组文档,如果说 MongoDB 中的文档类似于关系型数据库中的行,那么集合就如同表;

MongoDB 的单个实例可以容纳多个独立的数据库,每一个数据库都有自己的集合和权限。MongoDB 自带简洁但功能强大的JavaScript shell,这个工具对于管 MongoDB 实例和操作数据作用非常大;每一个文档都有一个特殊的键"_id",它在文档所处的集合中是唯一的,相当于关系数据库中的表的主键。

1.4 MongoDB 数据类

数据类型

描述

String

字符串。存储数据常用的数据类型。在 MongoDB 中,UTF-8 编码的字符串才是合法的。

Integer

整型数值。用于存储数值。根据你所采用的服务器,可分为 32 位

或 64 位。

Boolean

布尔值。用于存储布尔值(真/假)。

Double

双精度浮点值。用于存储浮点值。

Min/Max keys

将一个值与 BSON(二进制的 JSON)元素的最低值和最高值相对比。

Array

用于将数组或列表或多个值存储为一个键。

Timestamp

时间戳。记录文档修改或添加的具体时间。

Object

用于内嵌文档。

Null

用于创建空值。

Symbol

符号。该数据类型基本上等同于字符串类型,但不同的是,它一  般用于采用特殊符号类型的语言。

Date

日期时间。用 UNIX 时间格式来存储当前日期或时间。你可以指定自己的日期时间:创建 Date 对象,传入年月日信息。

Object ID

对象 ID。用于创建文档的 ID。

Binary Data

二进制数据。用于存储二进制数据。

Code

代码类型。用于在文档中存储 JavaScript 代码。

Regular expression

正则表达式类型。用于存储正则表达式。

 

2、索引基础知识

2.1、什么是索引

索引最常用的比喻就是书籍的目录,查询索引就像查询一本书的目录。本质上目录是将书中一小部分内容信息(比如题目)和内容的位置信息(页码)共同构成,而由于信息量小(只有题目),所以我们可以很快找到我们想要的信息片段,再根据页码找到相应的内容。同样索引也是只保留某个域的一部分信息(建立了索引的field的信息),以及对应的文档的位置信息。

例如:如下表所示(每行的数据在MongoDB中是存在于一个Document当中)

姓名

id

部门

籍贯

性别

年龄

熊琪

2

预研部

广西

18

秀雄

4

预研部

广西

23

天哥

3

预研部

湖北

23

腾腾

1

预研部

广东

22

 

假如我们想找id为3的document(即天哥的记录),如果没有索引,我们就需要扫描整个数据表,然后找出所有id为3的document。当数据表中有大量documents的时候,这个时间就会非常长(从磁盘上查找数据还涉及大量 的IO操作)。建立索引后会有什么变化呢?MongoDB会将id数据拿出来建立索引数据,如下

索引值

位置

1

pos4

2

pos1

3

pos3

4

pos2

 

为什么这样速度会快呢?这主要有几方面的因素

1.索引数据通过B+树来存储,从而使得搜索的时间复杂度为O(logdN)级别的(d是B+树的度, 通常d的值比较大,比如大于100),比原先O(N)的复杂度大幅下降。这个差距是惊人的,以一个实际例子来看,假设d=10,N=1亿,那么O(logdN) = 8(s), 而O(N)是1亿。是的,这就是算法的威力。

2.索引本身是在高速缓存当中,相比磁盘IO操作会有大幅的性能提升。(需要注意的是,有的时候数据量非常大的时候,索引数据也会非常大,当大到超出内存容量的时候,会导致部分索引数据存储在磁盘上,这会导致磁盘IO的开销大幅增加,从而影响性能,所以务必要保证有足够的内存能容下所有的索引数据)

当然,事物总有其两面性,在提升查询速度的同时,由于要建立索引,所以写入操作时就需要额外的添加索引的操作,这必然会影响写入的性能,所以当有大量写操作而读操作比较少的时候,且对读操作性能不需要考虑的时候,就不适合建立索引。当然,目前大多数互联网应用都是读操作远大于写操作,因此建立索引很多时候是非常划算和必要的操作。

2.2、MongoDB有哪些类型的索引

1 单键索引

单键索引(Single Field Indexes)顾名思义就是单个字段作为索引列,mongoDB的所有collection默认都有一个单键索引_id,我们也可以对一些经常作为过滤条件的字段设置索引,如给age字段添加一个索引,语法十分简单:

//给age字段添加升序索引

db.um_t_staff.createIndex({age:1})

        其中{age:1}中的1表示升序,如果想设置倒序索引的话使用 db.um_t_staff.createIndex({age:-1}) 即可。我们通过explain()方法查看查询计划,如下图,看到查询age=23的document时使用了索引,如果没有使用索引的话stage=COLLSCAN。

 

2 复合索引

复合索引(Compound Indexes)指一个索引包含多个字段,用法和单键索引基本一致。使用复合索引时要注意字段的顺序,如下添加一个name和age的复合索引,name正序,age倒序,document首先按照name正序排序,然后name相同的document按age进行倒序排序。mongoDB中一个复合索引最多可以包含32个字段。

//添加复合索引,name正序,age倒序

db.um_t_staff.createIndex({"name":1,"age":-1})

//过滤条件为name,或包含name的查询会使用索引(索引的第一个字段)

db.um_t_staff.find({name:'张三'}).explain()

db.um_t_staff.find({name:"张三",level:10}).explain()db.um_t_staff.find({name:"张三",age:23}).explain()

//查询条件为age时,不会使用上边创建的索引,而是使用的全表扫描

db.um_t_staff.find({age:23}).explain()

 

 

3 多键索引

多键索引(mutiKey Indexes)是建在数组上的索引,在mongoDB的document中,有些字段的值为数组,多键索引就是为了提高查询这些数组的效率。看一个栗子:准备测试数据,classes集合中添加两个班级,每个班级都有一个students数组,如下:

db.classes.insertMany([   {

  "classname":"class1",

 "students":[{name:'jack',age:20},

  {name:'tom',age:22},

 {name:'lilei',age:25}] },{"classname":"class2",

"students":[{name:'lucy',age:20},

{name:'jim',age:23}, {name:'jarry',age:26}]}] ))

 

 为了提高查询students的效率,我们使用  db.classes.createIndex({'students.age':1}) 给students的age字段添加索引,然后使用索引。

4 哈希索引

哈希索引(hashed Indexes)就是将field的值进行hash计算后作为索引,其强大之处在于实现O(1)查找,当然用哈希索引最主要的功能也就是实现定值查找,对于经常需要排序或查询范围查询的集合不要使用哈希索引。

2.3、mongoDB中常用的索引属性

1 唯一索引

唯一索引(unique indexes)用于为collection添加唯一约束,即强制要求collection中的索引字段没有重复值。添加唯一索引的语法:

//在um_t_staff的name字段添加唯一索引

db.um_t_staff.createIndex({name:1},{unique:true})

2 局部索引

     局部索引(Partial Indexes)顾名思义,只对collection的一部分添加索引。创建索引的时候,根据过滤条件判断是否对document添加索引,对于没有添加索引的文档查找时采用的全表扫描,对添加了索引的文档查找时使用索引。使用方法也比较简单:

//um_t_staff集合中age>25的部分添加age字段索引

 db.um_t_staff.createIndex( {age:1},

{ partialFilterExpression: {age:{$gt: 25 }}})

//查询age<25的document时,因为age<25的部分没有索引,会全表扫描查找(stage:COLLSCAN)db.um_t_staff.find({age:23})

//查询age>25的document时,因为age>25的部分创建了索引,会使用索引进行查找(stage:IXSCAN)

    db.um_t_staff.find({age:26})

当查询age=23的记录时,stage=COLLSCAN,当查询age=26的记录时,使用了索引

3 稀疏索引

  稀疏索引(sparse indexes)在有索引字段的document上添加索引,如在address字段上添加稀疏索引时,只有document有address字段时才会添加索引。而普通索引则是为所有的document添加索引,使用普通索引时如果document没有索引字段的话,设置索引字段的值为null。

  稀疏索引的创建方式如下,当document包含address字段时才会创建索引:

//创建在address上创建稀疏索引

  db.userinfos.createIndex({address:1},{sparse:true})

4 TTL索引

TTL索引(TTL indexes)是一种特殊的单键索引,用于设置document的过期时间,mongoDB会在document过期后将其删除,TTL非常容易实现类似缓存过期策略的功能。我们看一个使用TTL索引的栗子:

 //添加测试数据

db.logs.insertMany([

       {_id:1,createtime:new Date(),msg:"log1"},

       {_id:2,createtime:new Date(),msg:"log2"},

       {_id:3,createtime:new Date(),msg:"log3"},

       {_id:4,createtime:new Date(),msg:"log4"} ])

//在createtime字段添加TTL索引,过期时间是120s

db.logs.createIndex({createtime:1}, { expireAfterSeconds: 120 })

//logs中的document在创建后的120s后过期,会被mongoDB自动删除

注意:TTL索引只能设置在date类型字段(或者包含date类型的数组)上,过期时间为字段值+exprireAfterSeconds;document过期时不一定就会被立即删除,因为mongoDB执行删除任务的时间间隔是60s;capped Collection不能设置TTL索引,因为mongoDB不能主动删除capped Collection中的document。

九、采用 docker 方式安装部署

1、环境准备

准备一台服务器,虚拟机也行,linux 系统(centos 或者 ubuntu 都行)。分别装上环境 dockerdocker-compose。我用的是 Docker version 18.09.0, build 4d60db4docker-compose version 1.23.0-rc3, buildea3d406ecentos7.4

可以去官方镜像仓库拉取 docker pull mongo:4.0.5 或者后续我会把离线的 tar包上传到 github 上面,直接下载下来 docker load --input xxx.tar 到服务器即可完成准备之后,docker images 看一下,镜像是否搞定了。

 

2、编写docker-compose 文件,我这里命名mongodb.yml

 

3、部署运行

编写保存 mongodb.yml 文件之后。直接在当前目录下执行命令

$docker-compose -f mongodb.yml up -d

然后可能会出现这个报错

 问题不大,仔细看一下报错信息,就是说,找不到”base_net”这个 docker 卡,怎么办?直接创建出来呀,直接把我标注那一部分指令拷贝,出来执行一遍就可以了。

 

 OK,创建出来了,再次启动 docker-compose

$docker-compose -f mongodb.yml up -d

 

 看起来启动成功了,敲一下 docker ps 指令看看

 

  看到 STATUS 状态没有,如果没有一直在 restart 就说明跑起来了。现在我们可以用 mongodb 的可视化工具连接了,我用的是 Robot3T,你们也可以用别的工具,Studio3T 也不错,没有说规定用哪个,看个人喜好和习惯。就比如他们 mysql 都爱用 navicate(三叶草)的,我是喜欢用 SQLyog 的,毕竟比较简洁,navicate UI 做得比较好看一点,但是花里胡哨确实不太适合我。好了,扯了一堆有的没的。接下来,我用 Robot3T 连一下我们搭建的 mongodb 数据库

 

 

 Test 一下,没问题就连接进去看看

 到现在就可以看到, mongodb 的安装部署搭建完成啦, 是不是发现用docker-compose 非常简单(我这不是给 docker 打广告)

十、实战(springboot2.x 整合 mongodb)

准备

新建一个 springboot 项目,引入以下依赖

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<!-- mongo 核心依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>


<!-- lombok 工具类-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>

 

项目目录结构

 

配置

接下来我需要讲解的是 springboot 整合 mongodb 的配置。

application.yml 文件:

spring: 
data: mongodb:
#mongodb 服务的地址 host: 192.168.133.68
#mongodb 服务的端口
port: 27017

  #当前数据源连接的数据库

  database: mongodbDemo

server:

  port: 8089

 

mongodb 核心配置,MongoConfig.java

/**
*@description mongoDB 配 置
*@author Zhifeng.Zeng
*/
@Configuration @EnableConfigurationProperties(MongoProperties.class)
public class MongoConfig extends AbstractMongoConfiguration {

@Autowired
private MongoProperties properties;

@Override
public MongoClient mongoClient(){
return new MongoClient(properties.getHost(),properties.getPort());
}

@Override
public String getDatabaseName(){ return properties.getDatabase();
}

/**
*@description: 去除“_class”字段
*@return MappingMongoConverter
* @date 2018/08/15 16:24
*@author Rong.Jia
*@throws Exception
*/
@Bean @Override
public MappingMongoConverter mappingMongoConverter() throws Exception
{
MappingMongoConverter mmc = super.mappingMongoConverter(); mmc.setTypeMapper(new DefaultMongoTypeMapper(null));

Api 接口

这里我只编写了一个 UserController,实现了基本的 CRUD。

/**
*@author Zhifeng.Zeng
*@descrption
* @date 2020/05/05 11:20
*/

@RestController @RequestMapping("/user") public class UserController {

@Autowired
private UserService userService;

/**
*添加用户
*@param userDto
*/
@PostMapping
public ResponseVO addUser(@RequestBody UserDTO userDto){ userService.addUser(userDto);
return ResponseVO.success();
}

/**
*编辑用户
*@param userDto
*/
@PutMapping
public ResponseVO updateUser(@RequestBody UserDTO userDto){ userService.updateUser(userDto);
return ResponseVO.success();
}

/***删除用户
*@param userId
*/
@DeleteMapping("/{userId}")
public ResponseVO deleteUser(@PathVariable("userId") String userId){ userService.deleteUser(userId);
return ResponseVO.success();
}

/**
*查询用户
*/
@GetMapping
public ResponseVO<PageVO<UserVO>> findUsers(UserDTO userDTO){ PageVO<UserVO> userVoPageVo = userService.findUsers(userDTO); return ResponseVO.success(userVoPageVo);
}

这里所有的接口功能,我都已经在业务代码里实现了,接下来业务接口的相关实现的内容我会贴出来。

业务接口

Userservice.java

/**
*@author Zhifeng.Zeng
*@descrption 用户管理业务接口
* @date 2020/05/05 12:10
*/
public interface UserService {

/**
*添加用户
*@param userDto
*/
void addUser(UserDTO userDto);

/***编辑用户
*@param userDto
*/
void updateUser(UserDTO userDto);

/**
*删除用户
*@param userId
*/
void deleteUser(String userId);

/**
*查询用户
*/
PageVO<UserVO> findUsers(UserDTO userDTO);
}

UserserviceImpl.java

/**
*@author Zhifeng.Zeng
*@descrption 用户管理业务实现类
* @date 2020/05/05 12:11
*/
@Service
public class UserServiceImpl implements UserService {



@Autowired
private UserRepository userRepository;

@Override
public void addUser(UserDTO userDto) { User user = new User(); BeanUtils.copyProperties(userDto,user);
user.setTimestamp(System.currentTimeMillis()); userRepository.addUser(user);
}

@Override
public void updateUser(UserDTO userDto) {User user = new User();
user.setId(new ObjectId(userDto.getId())); BeanUtils.copyProperties(userDto,user); userRepository.updateUser(user);
}

@Override
public void deleteUser(String userId) { userRepository.deleteUser(userId);
}

@Override
public PageVO<UserVO> findUsers(UserDTO userDTO) {
PageVO<UserVO> pageVO = new PageVO();
Page<User> userPage = userRepository.findUsers(userDTO); List<User> userList = userPage.getContent(); if(userList!=null && userList.size()>0){
List<UserVO> userVoList = new ArrayList<>(); userList.forEach(user->{
UserVO userVo = new UserVO(); BeanUtils.copyProperties(user,userVo); userVo.setId(user.getId().toHexString()); userVoList.add(userVo);
});
pageVO.setRecords(userVoList);
}
pageVO.setTotalPages(userPage.getTotalPages()); pageVO.setTotal(userPage.getTotalElements()); pageVO.setIsLast(userPage.isLast()); pageVO.setIsFirst(userPage.isFirst()); pageVO.setHasPrevious(userPage.hasPrevious()); pageVO.setCurrentPage(userDTO.getCurrentPage()); pageVO.setPageSize(userDTO.getPageSize()); return pageVO;
}

持久层接口

我们做 javaEE 开发的,都知道,我们是遵循 mvc 架构的,mvc 架构不需要我解释了吧! 所以当然还有一层持久层。

UserRepository.java

/**
*@author Zhifeng.Zeng
*@descrption 用户管理持久化接口
* @date 2020/05/05 12:25
*/
public interface UserRepository {

/**
*添加用户
*@param user
*/
void addUser(User user);

/**
*编辑用户
*@param user
*/
void updateUser(User user);

/**
*删除用户
*@param userId
*/
void deleteUser(String userId);

/**
*查询用户
*/
Page<User> findUsers(UserDTO userDTO);
}

UserRepositoryImpl.java

/**
*@author Zhifeng.Zeng
*@descrption 用户管理持久化实现类
* @date 2020/05/05 12:25
*/
@Repository
public class UserRepositoryImpl implements UserRepository {
@Autowired
private MongoTemplate mongoTemplate;

@Override
public void addUser(User user) { mongoTemplate.insert(user);
}

@Override
public void updateUser(User user) {
Query updateQuery = new Query();

updateQuery.addCriteria(Criteria.where("_id").is(user.getId().toHexStri ng()));
Update update = new Update(); update.set("address",user.getAddress()); update.set("age",user.getAge()); update.set("name",user.getName()); update.set("sex",user.getSex()); mongoTemplate.updateMulti(updateQuery,update,User.class);
}

@Override
public void deleteUser(String userId) {
Query deleteQuery = new Query(); deleteQuery.addCriteria(Criteria.where("_id").is(userId)); mongoTemplate.remove(deleteQuery,User.class);
}

@Override
public Page<User> findUsers(UserDTO userDTO) {
Query query = new Query();

if(userDTO.getAddress() != null){
Pattern pattern = Pattern.compile("^.*" + userDTO.getAddress()
+ ".*$", Pattern.CASE_INSENSITIVE);
query.addCriteria(Criteria.where("address").regex(pattern));
}

if(userDTO.getAge() != null){ query.addCriteria(Criteria.where("age").is(userDTO.getAge()));

}
if(userDTO.getSex() != null){ query.addCriteria(Criteria.where("sex").is(userDTO.getSex()));
}

if(userDTO.getName() != null){ query.addCriteria(Criteria.where("name").is(userDTO.getName()));
}
// 排序方式
Sort sort = new Sort(Sort.Direction.DESC, "timestamp");
// 分页条件
Pageable pageable = PageRequest.of(userDTO.getCurrentPage(), userDTO.getPageSize(), sort);
List<User> userList = mongoTemplate.find(query.with(pageable),
User.class);
long count = mongoTemplate.count(query, User.class); return new PageImpl<>(userList,pageable,count);
}

 

源码

github地址:

https://github.com/githubzengzhifeng/springboot-mongodb

示例

Demo 代码大概就这么多,非常容易理解,接下来看看效果。这里我使用postman(接口测试工具)去对接口做一些简单的测试。先将服务跑起来

 

 

(1) 添加用户

 

 

 

 可以看到,返回成功,去看看数据库

 

 没有问题,为了方便后面调试,我多添加了几个。

 

 

(2) 不传任何条件检索

 

返回的 responseBody

{
"code": 200,
"data": {
"currentPage": 0, "hasPrevious": false, "isFirst": true, "isLast": true, "pageSize": 20, "records": [
{
"address": "湖北", "age": 23,
"id": "5eb11040ac2043855c8631f2",
"name": "小天",
"sex": "",
"timestamp": 1588662336103
},
{
"address": "湖南", "age": 23,
"id": "5eb11032ac2043855c8631f1",
"name": "小彬",
"sex": "",
"timestamp": 1588662322215
},
{
"address": "广东", "age": 23,
"id": "5eb11026ac2043855c8631f0",
"name": "小莫",
"sex": "",
"timestamp": 1588662310483
},
{
"address": "广西", "age": 23,
"id": "5eb11019ac2043855c8631ef",
"name": "小绿",
"sex": "",
"timestamp": 1588662297421
},
{
"address": "广西", "age": 25,
"id": "5eb1100bac2043855c8631ee", "name": "小红",
"sex": "",
"timestamp": 1588662283491
},
{
"address": "澳门", "age": 22,
"id": "5eb10f87ac2043855c8631ed", "name": "小白",
"sex": "",
"timestamp": 1588662151758
}
],
"total": 6,
"totalPages": 1
},
"message": "success"
}

(3) ”姓名”条件检索

 

 返回的 responseBody

{
"code": 200,
"data": {
"currentPage": 0, "hasPrevious": false, "isFirst": true, "isLast": true, "pageSize": 20,
"records": [
{
"address": "广东", "age": 23,
"id": "5eb11026ac2043855c8631f0",
"name": "小莫",
"sex": "",
"timestamp": 1588662310483
}
],
"total": 1,
"totalPages": 1
},
"message": "success"
}

 

(4) ”地址”条件检索(地址模糊查询)

 

返回的 responseBody

{
"code": 200,
"data": {
"currentPage": 0, "hasPrevious": false, "isFirst": true, "isLast": true, "pageSize": 20, "records": [
{
"address": "广东", "age": 23,
"id": "5eb11026ac2043855c8631f0",
"name": "小莫",
"sex": "",
"timestamp": 1588662310483
},
{
"address": "广西", "age": 23,
"id": "5eb11019ac2043855c8631ef",
"name": "小绿",
"sex": "",
"timestamp": 1588662297421
},
{
"address": "广西", "age": 25,
"id": "5eb1100bac2043855c8631ee", "name": "小红",
"sex": "",
"timestamp": 1588662283491
}
],
"total": 3,
"totalPages": 1
},
"message": "success"
}

 

(5) 编辑用户

这里,我们要把小白的年龄改成 25,地址改成香港

 

 

 

 可以看到,返回成功,去看看数据库。

 

 

 没有问题

(6) 删除用户

这里,我们要把小白这条信息删了

 

 可以看到,返回成功,去看看数据库。

 小白没了,说明删除成功。

 

总结

mongodb 的教程就暂时告一段落了,这只是开始,只是入门的基础。像mongodb 还支持复杂的嵌套对象及嵌套查询、聚合统计查询;高可用(Replica 副本集或 Master-Slave 主从复制)sharding 分片等等技术,我后续也会把内容做出来。但是,俗话说得好,知识无底,学海无涯,成长还得靠自己。行动起来吧,实践才是检验真理的唯一标准,你们也许会在无数次踩坑中学会填坑的。

备注:本文理论主要参考mongodb中文官网

host: 192.168.133.68