NoSQL:Not Only SQL ,其本质还是一种数据库技术,但相比传统数据库它可以不会遵循一些约束,比如:sql标准、ACID属性,表结构等。
特点
缺点
具体分类
权威数据库排行:https://db-engines.com/en/ranking
为啥是MongoDB?答:流行
标准的关系型数据库
*
MongoDB数据库
*
采用bson结构存储, 建模方式自然而不失直观
1:多样性:同一个集合(表)异构数据(不同字段的文档对象)
*
2:动态性:需求变更,字段可更加需求动态拓展
*
3:数据治理:支持Json Schema规范,限制字段类型,在保证模型的灵活性前提下,保证数据安全
*
虽说mongodb很好用,并不是一定要选,开发中需要综合考量(包括学习、开发、运维等成本)
应用特征 | Yes / No |
---|---|
应用不需要事务及复杂 join 支持 | 必须 Yes |
新应用,需求会变,数据模型无法确定,想快速迭代开发 | ? |
应用需要2000-3000以上的读写QPS(更高也可以) | ? |
应用需要TB甚至 PB 级别数据存储 | ? |
应用发展迅速,需要能快速水平扩展 | ? |
应用要求存储的数据不丢失 | ? |
应用需要99.999%高可用 | ? |
应用需要大量的地理位置查询、文本查询 | ? |
如果上述有1个 Yes,可以考虑 MongoDB,2个及以上的 Yes,选择MongoDB绝不会错
MongoDB 的应用已经渗透到各个领域,比如游戏、物流、电商、内容管理、社交、物联网、视频直播等,以下是几个实际的应用案例
MongoDB:是一个数据库 ,高性能、无模式、文档性,目前nosql中最热门的数据库,开源产品,基于c++开发。是nosql数据库中功能最丰富,最像关系数据库的。
特点:
概括起来:
MongoDB能存:海量,不敏感,又要求一定查询性能的数据
下载:https://www.mongodb.com/download-center/community
说明:Linux版的安装,这里不展开讲,后续讲Linux操作系统时再讲,此处重点讲Window版的安装
Window安装版有2种方式:
下载后双击安装,然后一路next,next即可,小白首选。
*
下载后解压配置
步骤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服务器自带客户端: MongoDB Shell
*
官方推荐:compass
下载:https://www.mongodb.com/products/compass
个人感觉不是很好用,反而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没有专门的创建数据库的命令, 可以使用use 来选择某个数据库, 如果库不存在, 将会创建,但是只有往该库加入文档后才保存成文件
*
db.dropDatabase(); //删除当前所在数据库
MongoDB中,不用创建集合, 因为没有固定的结构, 直接使用db.集合名称.命令 来操作就可以了, 如果非要显示创建的话使用:
db.createCollection(“集合名称”)
*
show collections 或者 show tables
*
语法:db.集合名.drop();
db.users.drop()
*
这里注意,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的数据文档
这里重点讲解,springboot集成MongoDB的操作
需求:完成员工的CRUD
步骤1:建立项目mongo-demo
步骤2:导入依赖
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.4.3version>
<relativePath/>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-mongodbartifactId>
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:创建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....
*
*/
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);
}