MongoDB
NoSQL
NoSQL:Not Only SQL ,其本质还是一种数据库技术,但相比传统数据库它可以不会遵循一些约束,比如:sql标准、ACID属性,表结构等。
特点
- 满足对数据库的高并发读写
- 对海量数据的高效存储和访问
- 对数据库高扩展性和高可用性
- 灵活的数据结构,满足数据结构不固定的场景
缺点
- 一般不支持事务
- 实现复杂SQL查询比较复杂
- 运维人员数据维护门槛较高
- 目前不是主流的数据库技术
具体分类
为什么是MongoDB
权威数据库排行:https://db-engines.com/en/ranking
为啥是MongoDB?答:流行
MongoDB简介
MongoDB优势
建模简单而直观
标准的关系型数据库
*
MongoDB数据库
*
采用bson结构存储, 建模方式自然而不失直观
建模灵活可拓展
1:多样性:同一个集合(表)异构数据(不同字段的文档对象)
*
2:动态性:需求变更,字段可更加需求动态拓展
*
3:数据治理:支持Json Schema规范,限制字段类型,在保证模型的灵活性前提下,保证数据安全
*
API自然,快速开发
原生的高可用与易扩展
MongoDB适用场景
要不要选MongoDB
虽说mongodb很好用,并不是一定要选,开发中需要综合考量(包括学习、开发、运维等成本)
应用特征 | Yes / No |
---|---|
应用不需要事务及复杂 join 支持 | 必须 Yes |
新应用,需求会变,数据模型无法确定,想快速迭代开发 | ? |
应用需要2000-3000以上的读写QPS(更高也可以) | ? |
应用需要TB甚至 PB 级别数据存储 | ? |
应用发展迅速,需要能快速水平扩展 | ? |
应用要求存储的数据不丢失 | ? |
应用需要99.999%高可用 | ? |
应用需要大量的地理位置查询、文本查询 | ? |
如果上述有1个 Yes,可以考虑 MongoDB,2个及以上的 Yes,选择MongoDB绝不会错
MongoDB使用场景
MongoDB 的应用已经渗透到各个领域,比如游戏、物流、电商、内容管理、社交、物联网、视频直播等,以下是几个实际的应用案例
- 游戏场景,使用 MongoDB 存储游戏用户信息,用户的装备、积分等直接以内嵌文档的形式存储,方便查询、更新
- 物流场景,使用 MongoDB 存储订单信息,订单状态在运送过程中会不断更新,以 MongoDB 内嵌数组的形式来存储,一次查询就能将订单所有的变更读取出来。
- 社交场景,使用 MongoDB 存储存储用户信息,以及用户发表的朋友圈信息,通过地理位置索引实现附近的人、地点等功能
- 物联网场景,使用 MongoDB 存储所有接入的智能设备信息,以及设备汇报的日志信息,并对这些信息进行多维度的分析
- 视频直播,使用 MongoDB 存储用户信息、礼物信息等.
MongoDB不使用的场景
- 对事务要求较高的系统:例如银行、财务、金融类等系统。MongoDB对事物的支持较弱;
- 传统的对SQL要求较高的系统:特定问题的数据分析,多数据实体关联,
- 涉及到复杂的、高度优化的查询方式;
- 较为简单系统;数据结构相对固定,使用sql进行查询统计更加便利的时候;
MongoDB小结
MongoDB:是一个数据库 ,高性能、无模式、文档性,目前nosql中最热门的数据库,开源产品,基于c++开发。是nosql数据库中功能最丰富,最像关系数据库的。
特点:
- 面向集合文档的存储:适合存储Bson(json的扩展)形式的数据;
- 格式自由,数据格式不固定,生产环境下修改结构都可以不影响程序运行;
- 强大的查询语句,面向对象的查询语言,基本覆盖sql语言所有能力;
- 完整的索引支持,支持查询计划;
- 支持复制和自动故障转移;
- 支持二进制数据及大型对象(文件)的高效存储;
- 使用分片集群提升系统扩展性;
- 使用内存映射存储引擎,把磁盘的IO操作转换成为内存的操作;
概括起来:
MongoDB能存:海量,不敏感,又要求一定查询性能的数据
MongoDB安装
服务端
下载:https://www.mongodb.com/download-center/community
说明:Linux版的安装,这里不展开讲,后续讲Linux操作系统时再讲,此处重点讲Window版的安装
Window安装版有2种方式:
方式1:安装版
下载后双击安装,然后一路next,next即可,小白首选。
*
方式2:绿色版
下载后解压配置
步骤1:解压创建2个目录
在解压后的根目录下创建2个目录夹
data/db:用于存放数据库信息
logs:存放运行日志
*
步骤2:进入bin目录,配置配置文件:mongo.conf
# 数据库文件路径
dbpath=D:\OpenSource\mongodb-4.2.2\data\db
# 日志文件
logpath=D:\OpenSource\mongodb-4.2.2\logs\mongo.log
# 日志采用追加模式,配置后mongodb日志会追加到现有的日志文件,不会重新创建一个新文件
logappend=true
# 启用日志文件,默认启用
journal=true
# 这个选项可以过滤掉一些无用的日志信息,若需要调试使用请设置为 false
quiet=true
# 端口号 默认为 27017
port=27017
注意:重点修改dbpath 跟 logpath路径,分别指向步骤1中创建的目录。
步骤3:写一个启动脚本:statup.bat
mongod --config ./mongo.conf
步骤4:双击启动statup.bat命令
*
客户端
MongoDB客户端有非常多选择:
MongoVUE RockMongo Studio 3T compass Navicat for MongoDB MongoDB shell
MongoDB Shell
MongoDB服务器自带客户端: MongoDB Shell
*
compass
官方推荐:compass
下载:https://www.mongodb.com/products/compass
个人感觉不是很好用,反而Navicat for MongoDB这个相对好用,所以此处选择Navicat for MongoDB作为客户端
Navicat for MongoDB
下载:http://www.navicat.com.cn/products
Navicat目前企业版(可试用14天)最新是16,MongoDB支持至少12版本,大家根据自己的需要选择。最后强调:支持正版
后续我们使用Navicat Premium 12版本,如果之前有安装过12 或者 12以上可以不用重新安装。
安装步骤:略:无脑next,next,next。
安装成功之后打开:
*
创建连接成功之后,默认是看不了数据库的,需要点击查看:显式隐藏项目,才能看到MongoDB系统自带的数据库。
到这,MongoDB安装算成功啦。
核心概念
对MongoDB有了大体了解之后,接下来就是MongoDB基本操作了。操作前希望时刻记得:MongoDB是一个数据库,那么它不会脱离数据库的几个核心概念:数据库,表,列,行。
概念
案例
*
至于怎么定义数据库名, 集合名,字段名,跟之前MySQL定义数据库名,表名,列名遵循的规则一样
数据类型
MongoDB支持的数据类型有很多,不需要记忆所有,关注常用的即可
数据类型 | 描述 | 举例 |
---|---|---|
null | 表示空值或者未定义的对象 | {“x”: null} |
boolean | true/false | {“x”: true} |
int | 32位整数 | {“x”: NumberInt(“3”)} |
long | 64位整数 | {“x”: NumberLong(“3”)} |
double | 浮点数 | {“x”:3.14, “y”:3} |
string | UTF-8字符串 | {“x”:“dafei”} |
_id | 12字节的唯一id,自动生成 | {“_id”:ObjectId(“5e2ab4f48847000059006f73”)} |
date | 从标准纪元开始的毫秒数 | {“date”:ISODate(“2018-11-26T00:00:00.000Z”)} |
Regular expression | 正则表达式,语法同js的正则对象 | {“x”: /foobar/i} |
code | JavaScript代码块 | {“x”:function(){…}} |
undefined | 未定义 | {“x”:undefined} |
array | 值集合或者列表 | {“x”:[“a”,“b”]} |
object | 文档中嵌入另外一个文档 | {“x”:{“a”:1, “b”:2}} |
数据库与集合
官网操作文档:https://docs.mongodb.com/manual/introduction/
MongoDB Shell方式
创建数据库
*
MongoDB没有专门的创建数据库的命令, 可以使用use 来选择某个数据库, 如果库不存在, 将会创建,但是只有往该库加入文档后才保存成文件
*
删除数据库
db.dropDatabase(); //删除当前所在数据库
创建集合
MongoDB中,不用创建集合, 因为没有固定的结构, 直接使用db.集合名称.命令 来操作就可以了, 如果非要显示创建的话使用:
db.createCollection(“集合名称”)
*
查看集合
show collections 或者 show tables
*
删除集合
语法:db.集合名.drop();
db.users.drop()
*
Navicat方式
创建数据库
创建集合
这里注意,MongoDB的集合最初没有任何字段,所以默认是一个空,里面都没有。
文档操作
为了开发方便, 建议使用Navicat的方式操作MongoDB命令。另外学习时,建议新建并保存一个查询文件,方便后续复习。
题外话: MongoDB 语法使用JavaScript引擎进行解析的,如果你js学得还可以,那你就掌握了先手优势啦。
把港股创建的查询文件保存到桌面,结果是js后缀。
*
文档添加
语法
db.集合名.insert( {文档} ) //插入单个
db.集合名.insert( [{文档1},{文档2}] ) //插入多个
注意
1:往集合中新增文档,当集合不存在时会自动先创建集合
2:当操作成功时,集合会给文档生成一个**_id**字段,也可以自己指定
练习
//添加一个用户: 用户(id:1, name:"dafei", age:18)
db.users.insert({id: 1, name: "dafei", age: 18})
//添加多个用户:用户(id:2, name:"xiaofei", age:16) 用户(id:3, name:"laofei", age:88)
db.users.insert([{id:2, name:"xiaofei", age:16}, {id:3, name:"laofei", age:88}])
查看
*
换种风格
分别为:网格视图, 树视图,JSON视图
*
文档更新
语法
//update users set xx=1 where xx = 1
//更新单个
db.集合名.updateOne(
{query},
{update}
)
//更新多个
db.集合名.updateMany(
{query},
{update}
)
query : update的查询条件,类似sql update查询内where后面的。
update : update的对象和一些更新的操作符(如
,
,
,inc…)等,也可以理解为sql update查询内set后面的
练习
//把一个带有name=dafei的文档,修改其age值为30
db.users.updateOne({name: "dafei"}, {$set: {age: 30}})
//修改所有name=dafei的文档,修改其name=大飞,age=20
db.users.updateMany({name: "dafei"}, {$set: {name: "大飞", age: 20}})
//修改所有的文档,修改其name=xxx,age=10
db.users.updateMany({}, {$set: {name: "xxx", age: 10}})
文档删除
语法
//删除1个:
db.集合名.deleteOne(
{query}
)
//删除多个:
db.集合名.deleteMany(
{query}
)
练习
//删除_id=xxx的文档
db.users.deleteOne({_id: ObjectId("xxx")})
//删除所有带有name=dafei的文档
db.users.deleteMany({name: "bunny"})
//删除当前数据库中所有文档
db.users.deleteMany({})
文档查询
数据准备
db.users.insert({"id":NumberLong(1),"name":"dafei","age":NumberInt(18)})
db.users.insert({"id":NumberLong(2),"name":"xiaofei","age":NumberInt(20)})
db.users.insert({"id":NumberLong(3),"name":"zhang quan dan","age":NumberInt(33)})
db.users.insert({"id":NumberLong(4),"name":"zhang kun","age":NumberInt(26)})
db.users.insert({"id":NumberLong(5),"name":"zhang han yun","age":NumberInt(29)})
db.users.insert({"id":NumberLong(6),"name":"cai xv kun","age":NumberInt(29)})
db.users.insert({"id":NumberLong(7),"name":"jia nai liang","age":NumberInt(25)})
db.users.insert({"id":NumberLong(8),"name":"fu rong wang","age":NumberInt(28)})
db.users.insert({"id":NumberLong(9),"name":"wang da","age":NumberInt(31)})
db.users.insert({"id":NumberLong(10),"name":"da wang","age":NumberInt(32)})
db.users.insert({"id":NumberLong(11),"name":"will","age":NumberInt(26)})
查询全部
语法
//sql: select age, name from table where ....
db.集合名.find({query}, {projection})
query: 是条件
projection: 列的投影,指定返回哪些列 _id默认返回 eg: {“_id”: 0}
练习
查所有用户
//sql: select * from users;
db.users.find()
db.users.find({}) //{} 是条件
db.users.find({}, {name:1}) //第二个{} 表示查询哪些列
排序
语法
db.集合名.find(query, projection).
sort( {列:1} ) //正序
sort( {列:-1} ) //倒序
多列排序
sort( {列1:1, 列2:1} )
练习
//查询所有用户按年龄排序
//sql: select * from users order by age desc|asc
db.users.find({}).sort({age:1}) //正序
db.users.find({}).sort({age:-1}) //倒序
db.users.find({}).sort({age:-1, id:1}) //多列排序
分页
语法
db.集合名.find({}).skip(n).limit(m)
跟mysql中 limit ? ? 一一对应
练习
//分页查询所有用户
//约定每页显示3条
//sql: select * from users limit 0, 3
db.users.find().skip(0).limit(3) //第一页
db.users.find().skip(3).limit(3) //第二页
...
db.users.find().skip((currentPage-1) * pageSize).limit(pageSize)//第n页
条件查询
比较运算符
*
语法
db.集合名. find( { 字段: {比较操作符: 值, ...} } )
比较操作符 | 解释 |
---|---|
$gt | 大于 |
$lt | 小于 |
$gte | 大于等于 |
$lte | 小于等于 |
$ne | 不等 |
$in | 在…中 |
练习
查询age > 30的用户
//sql: select * from users where age > 300
db.users.find(
{age: { $gt:30}}
)
查询名字为 dafei 或xiaofei用户
//sql: select * form users where name in("dafei", "xiaofei")
db.users.find(
{name: {$in: ["dafei", "xiaofei"]}}
)
逻辑运算符【拓展】
*
语法
db.集合.find( {逻辑操作符: [条件1, 条件2, ...]} )
比较操作符 | 解释 |
---|---|
$and | && |
$or | || |
$not | ! |
练习
//查年龄在28 到 30间的用户信息
//sql: select * from users where age >= 28 and age <=30
db.users.find(
{age:{$gte:28, $lte:30}}
)
db.users.find(
{
$and:[{age:{$gte:28}},{age:{$lte:30}}]
}
)
//查看年龄小于28或者年龄大于30用户信息
//sql: select * from users where age <28 or age >30
db.users.find(
{$or: [{age: {$lt: 28}}, {age: {$gt:30}}]}
)
//查看年龄等于28或者等于30用户信息
//sql: select * from users where age =28 or age =30
db.users.find(
{$or: [{age:28}, {age: 30}]}
)
模糊查询【拓展】
语法
db.集合.find({列: {$regex: /关键字/}}) //正则对象
db.集合.find({列: {$regex: "关键字"}}) //正则表达式
{name:/xxx/} --->%xxx%
{name:/^xxx/} --->xxx%
{name:/xxx$/} --->%xxx
{name:/xxx/i} 忽略大小写
练习
//查询name带有fei的用户信息
//sql: select * from users where name like '%fei%'
db.users.find(
{name: {$regex:/fei/ }}
)
//查name中包含fei字样,并且年龄在28 到 30间的用户信息,
//sql: select * from users where name like '%fei%' and age >= 28 and age <=30
db.users.find(
{$and: [{name: {$regex:/fei/}}, {age: {$gte:28, $lte:30}}]}
)
文档设计
MongoDB文档设计跟普通的关系型数据库表设计类型,但是涉及到关联关系时,需要额外处理。
文档嵌套
一对一关系 与 多对一关系
*
数组方式
一对多关系与多对多关系
操作技巧:关系型数据库实体对象toJsonString之后就是MongoDB的数据文档
MongoDB 编程
这里重点讲解,springboot集成MongoDB的操作
基本CRUD
需求:完成员工的CRUD
步骤1:建立项目mongo-demo
步骤2:导入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.3</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--spring boot data mongodb-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
</dependencies>
步骤3:配置mongodb url
# application.properties
# 配置数据库连接
#格式: mongodb://账号:密码@ip:端口/数据库?认证数据库
#spring.data.mongodb.uri=mongodb://root:admin@localhost/mongodemo?authSource=admin
spring.data.mongodb.uri=mongodb://localhost/mongodemo
# 配置MongoTemplate的执行日志
logging.level.org.springframework.data.mongodb.core=debug
步骤3:编写domain实体类
@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
@ToString
@Document("users") //设置文档所在的集合
public class User {
//文档的id使用ObjectId类型来封装,并且贴上@Id注解,
// 自动映射为_id 自动封装ObjectId
@Id
private String id;
private String name;
private Integer age;
private List<String> hobby = new ArrayList<>();
}
@Id 主键id,将id映射成集合中_id列
步骤4:编写持久层接口
/**
* MongoDB自定义对象的持久层接口
* 1>定义接口继承MongoRepository
* 2>明确指定2个泛型
* 泛型1:当前接口操作实体对象:User
* 泛型2:当前接口操作实体对象对应主键属性类型:id
*/
public interface UserMongoRepository extends MongoRepository<User, String> {
}
步骤5:编写service接口与实现类
public interface IUserService {
void save(User user);
void delete(String id);
void update(User user);
User get(String id);
List<User> list();
}
@Service
public class UserServiceImpl implements IUserService{
@Autowired
private UserRepository userRepository;
@Override
public void save(User user) {
userRepository.save(user);
}
@Override
public void delete(String id) {
userRepository.deleteById(id);
}
@Override
public void update(User user) {
userRepository.save(user);
}
@Override
public User get(String id) {
return userRepository.findById(id).get();
}
@Override
public List<User> list() {
return userRepository.findAll();
}
}
步骤6:CRUD测试
@SpringBootTest
public class UserTest {
@Autowired
private IUserService userService;
@Test
public void testSave(){
User user = new User();
user.setName("dafei");
user.setAge(18);
userService.save(user);
}
@Test
public void testUpdate(){
User user = new User();
user.setId("5de507fca0852c6c7ebc1eac");
user.setName("dafei2222");
user.setAge(18);
userService.update(user);
}
@Test
public void testDelete(){
userService.delete("5de507fca0852c6c7ebc1eac");
}
@Test
public void testGet(){
System.out.println(userService.get("5de507fca0852c6c7ebc1eac"));
}
@Test
public void testList(){
System.out.println(userService.list());
}
}
基本查询
需求:查询name=dafei数据
方案1:JPA 方式
步骤1:创建QueryTest
@SpringBootTest
public class QueryTest {
//需求:查询name = dafei用户信息
//MQL: db.users.find({name:"dafei"})
@Autowired
private UserRepository repository;
@Test
public void testQuery(){
User user = repository.findByName("dafei");
System.err.println(user);
}
}
步骤2:修改UserRepository接口
public interface UserRepository extends MongoRepository<User, String> {
//通过name查询员工数据
User findByName(String name);
}
解析:
/**
* Spring-Data-mongodb 实现了JPA 语法规则,所有可以使用JPA查询方法定义规则来实现快速查询
*
* JPA查询方法定义规则:
* 前缀 + 操作符 + 属性/列
*
* 前缀: find / query
* 操作符: by and or lessthan like....
*
*/
方案2:MongoTemplate方式
JPA查询语法针对简单条件查询还是可行,但是,如果涉及到高级查询(多条件查询)就无能为力, 怎么办呢?
使用spring-data-mongodb 另外一套操作api—MongoTemplate
需求:查询name=dafei数据
@SpringBootTest
public class QueryTest {
@Autowired
private MongoTemplate template;
@Test
public void testQuery2(){
//类似: mybatyis-plus 条件构造器--Wrapper
Criteria criteria = Criteria.where("name").is("dafei");
// 创建查询对象----MQL语句抽象对象
Query query = new Query();
// 添加限制条件
query.addCriteria(criteria);
List<User> list = template.find(query, User.class, "users");
list.forEach(System.err::println);
}
}
分页查询
@Autowired
private MongoTemplate mongoTemplate;
// 分页查询文档,显示第2页,每页显示3个,按照id升序排列
@Test
public void testQuery1() throws Exception {
// 创建查询对象
Query query = new Query();
// 设置分页信息
query.skip(3).limit(3);
// 设置排序规则
query.with(Sort.by(Sort.Direction.ASC,"id"));
List<User> list = mongoTemplate.find(query, User.class, "users");
list.forEach(System.err::println);
}