62-探花04-MongoDB入门(第4章)
目录
第 4 章 MongDB 入门
今日内容介绍
学习目标
- MongoDB 环境搭建
- MongoDB 基本 CRUD 操作
- 通过 JavaApi 操作 MongoDB
- SpringBoot 整合 MongoDB
1. MongoDB 入门
【目标】
了解 MongoDB
MongoDB 基础操作(等同于 mysql sql 脚本)
JavaApi 操作 MongoDB
【路径】
1:社交类软件功能特点分析
2:MongoDB 介绍
3:MongoDB 基础操作
4:通过 JavaApi 操作 MongoDB
【讲解】
对于社交类软件的功能,我们需要对它的功能特点做分析:
- 数据量会随着用户数增大而增大
- 读多写少
- 价值较低
- 非好友看不到其动态内容
- ……
针对以上特点,我们来分析一下:
- mysql:关系型数据库
- redis:redis 可以
- 对于数据量大而言,显然不能够使用关系型数据库进行存储,我们需要通过 MongoDB 进行存储
- 对于读多写少的应用,需要减少读取的成本
- 比如说,一条 SQL 语句,单张表查询一定比多张表查询要快
探花交友项目:
- mysql 数据库:重要的数据
- redis 数据:加快查询速度
- mongodb:承担社交的业务功能
1.1. MongoDB 简介
MongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。
MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的,它支持的数据结构非常松散,是类似 json 的bson 格式,因此可以存储比较复杂的数据类型。
MongoDB最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。
1.1.1. MongoDB 的特点
MongoDB 最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。它是一个面向集合的, 模式自由的文档型数据库。具体特点总结如下:
- 面向集合 (mysql table 表) 存储,易于存储对象类型的数据
- 模式自由
- 支持动态查询
- 支持完全索引(加快查询速度),包含内部对象
- 支持复制和故障恢复
- 使用高效的二进制数据存储,包括大型对象(如视频等)
- 自动处理碎片,以支持云计算层次的扩展性
- 支持 Python,PHP,Ruby,Java,C,C#,Javascript,Perl 及 C++ 语言的驱动程 序, 社区中也提供了对 Erlang 及.NET 等平台的驱动程序
- 文件存储格式为 BSON(一种 JSON 的扩展)
1.1.2. 通过 docker 安装 MongoDB
在课程资料的虚拟机中已经提供了 MongoDB 的镜像和容器,我们只需要使用简单的命令即可启动
#进入 base 目录
cd /root/docker-file/base/
#批量创建启动容器,其中已经包含了 redis,zookeeper,mongodb 容器
docker-compose up -d
#查看容器
docker ps -a
可以看到 mongoDB 已经启动,对外暴露了 27017 的操作端口
1.1.3. MongoDB 体系结构
MongoDB 的逻辑结构是一种层次结构。主要由: 文档 (document)、集合(collection)、数据库(database) 这三部分组成的。逻辑结构是面 向用户的,用户使用 MongoDB 开发应用程序使用的就是逻辑结构。
- MongoDB 的文档(document),相当于关系数据库中的一行记录。
- 多个文档组成一个集合(collection),相当于关系数据库的表。
- 多个集合(collection),逻辑上组织在一起,就是数据库(database)。
- 一个 MongoDB 实例支持多个数据库(database)。 文档 (document)、集合(collection)、数据库(database) 的层次结构如下图:
为了更好的理解,下面与 SQL 中的概念进行对比:
SQL 术语 / 概念 | MongoDB 术语 / 概念 | 解释 / 说明 |
---|---|---|
database | database | 数据库 |
table | collection | 数据库表 / 集合 |
row | document | 数据记录行 / 文档 |
column | field | 数据字段 / 域 |
index | index | 索引 |
table joins | 表连接,MongoDB 不支持 | |
primary key | primary key | 主键,MongoDB 自动将 _id 字段设置为主键 |
1.1.4. 数据类型
- null:用于表示空值或者不存在的字段,
- 布尔型:布尔类型有两个值 true 和 false,
- 数值:shell 默认使用 64 为浮点型数值。{“x”:3.14} 或 {“x”:3}。对于整型值,可以使用 NumberInt(4 字节符号整数)或 NumberLong(8 字节符号整数),
- 字符串:UTF-8 字符串都可以表示为字符串类型的数据,
- 日期:日期被存储为自新纪元依赖经过的毫秒数,不存储时区,
- 正则表达式:查询时,使用正则表达式作为限定条件,语法与 JavaScript 的正则表达式相 同,
- 数组:数据列表或数据集可以表示为数组,
- 内嵌文档:文档可以嵌套其他文档,被嵌套的文档作为值来处理,{“x”:{“y”:3}}
- 对象 Id:对象 id 是一个 12 字节的字符串,是文档的唯一标识,
- 二进制数据:二进制数据是一个任意字节的字符串。它不能直接在 shell 中使用。如果要 将非 utf- 字符保存到数据库中,二进制数据是唯一的方式。
1.2. MongoDB 基本操作
1.2.1. 数据库以及表的操作
#查看所有的数据库
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
#通过 use 关键字切换数据库
> use admin
switched to db admin
#创建数据库
#说明:在 MongoDB 中,数据库是自动创建的,通过 use 切换到新数据库中,进行插入数据即可自动创建数据库
> use testdb
switched to db testdb
> show dbs #并没有创建数据库
admin 0.000GB
config 0.000GB
local 0.000GB
> db.user.insert({id:1,name:'zhangsan'}) #插入数据
WriteResult({ "nInserted" : 1 })
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
testdb 0.000GB #数据库自动创建
#查看表
> show tables
user
> show collections
user
>
#删除集合(表)
> db.user.drop()
true #如果成功删除选定集合,则 drop() 方法返回 true,否则返回 false。
#删除数据库
> use testdb #先切换到要删除的数据中
> db.dropDatabase() #删除数据库
> show dbs
1.2.2. 新增数据
在 MongoDB 中,存储的文档结构是一种类似于 json 的结构,称之为 bson(全称为:Binary JSON)。
#插入数据
#语法:db. 表明.insert(json 字符串)
> db.user.insert({id:1,username:'zhangsan',age:20})
> db.user.save({id:2,username:'lisi',age:25})
> db.user.find() #查询数据
1.2.3. 更新数据
update() 方法用于更新已存在的文档。语法格式如下:
db.collection.update(
<query>,
<update>,
[
upsert: <boolean>
]
)
参数说明:
- query : update 的查询条件,类似 sql update 查询内 where 后面的。
- update : update 的对象和一些更新的操作符(如 $,$inc.$set)等,也可以理解为 sql update 查询内 set 后面的
- upsert : 可选,这个参数的意思是,如果不存在 update 的记录,是否插入 objNew,true 为插入,默认是 false,不插入。
#查询全部
> 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)
1.2.4. 删除数据
通过 remove() 方法进行删除数据,语法如下:
db.collection.remove(
<query>,
{
justOne: <boolean>
}
)
参数说明:
- query :(可选)删除的文档的条件。
- justOne : (可选)如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值 false,则删除所有匹配条件的文档。
实例:
#删除数据
> 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({})
#说明:为了简化操作,官方推荐使用 deleteOne()与 deleteMany() 进行删除数据操作。
db.user.deleteOne({id:1})
db.user.deleteMany({}) #删除所有数据
1.2.5. 查询数据
MongoDB 查询数据的语法格式如下:
db.user.find([query],[fields])
- query :可选,使用查询操作符指定查询条件
- fields :可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)。
实例:
#插入测试数据
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({age:{$lte:21}, id:{$gte:2}}) #and查询,age小于等于21并且id大于等于2
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}) #按照age倒序排序,-1为倒序,1为正序
1.2.6. 索引
索引通常能够极大的提高查询的效率,如果没有索引,MongoDB 在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。
这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。
索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构
#创建索引
> db.user.createIndex({'age':1})
#查看索引
> db.user.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "testdb.user"
}
]
#说明:1 表示升序创建索引,-1 表示降序创建索引。
1.2.7. 执行计划
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 进行查询
sql 语句如何优化?加索引
1.2.8. UI 客户端工具
免费客户端:
Robo 3T 是 MongoDB 的客户端工具,我们可以使用它来操作 MongoDB。
查看数据:
收费客户端:(免费试用 1 个月)
studio-3t-x64 是 MongoDB 的客户端工具,我们可以使用它来操作 MongoDB。
查看数据:
1.3. 通过 JavaApi 操作 MongoDB(了解)
1.3.1. 创建工程
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
</parent>
<span class="hljs-tag"><<span class="hljs-name">dependencies</span>></span>
<span class="hljs-tag"><<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.mongodb<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>mongodb-driver-sync<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">version</span>></span>3.9.1<span class="hljs-tag"></<span class="hljs-name">version</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>junit<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>junit<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">version</span>></span>4.12<span class="hljs-tag"></<span class="hljs-name">version</span>></span>
<span class="hljs-tag"><<span class="hljs-name">scope</span>></span>test<span class="hljs-tag"></<span class="hljs-name">scope</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.projectlombok<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>lombok<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">version</span>></span>1.18.4<span class="hljs-tag"></<span class="hljs-name">version</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependency</span>></span>
<span class="hljs-comment"><!--
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
--></span>
<span class="hljs-tag"></<span class="hljs-name">dependencies</span>></span>
<span class="hljs-tag"><<span class="hljs-name">build</span>></span>
<span class="hljs-tag"><<span class="hljs-name">plugins</span>></span>
<span class="hljs-comment"><!-- java编译插件 --></span>
<span class="hljs-tag"><<span class="hljs-name">plugin</span>></span>
<span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.apache.maven.plugins<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>maven-compiler-plugin<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">version</span>></span>3.2<span class="hljs-tag"></<span class="hljs-name">version</span>></span>
<span class="hljs-tag"><<span class="hljs-name">configuration</span>></span>
<span class="hljs-tag"><<span class="hljs-name">source</span>></span>1.8<span class="hljs-tag"></<span class="hljs-name">source</span>></span>
<span class="hljs-tag"><<span class="hljs-name">target</span>></span>1.8<span class="hljs-tag"></<span class="hljs-name">target</span>></span>
<span class="hljs-tag"><<span class="hljs-name">encoding</span>></span>UTF-8<span class="hljs-tag"></<span class="hljs-name">encoding</span>></span>
<span class="hljs-tag"></<span class="hljs-name">configuration</span>></span>
<span class="hljs-tag"></<span class="hljs-name">plugin</span>></span>
<span class="hljs-tag"></<span class="hljs-name">plugins</span>></span>
<span class="hljs-tag"></<span class="hljs-name">build</span>></span>
1.3.2. 编写 Demo
该 demo 中演示了,如何连接到 MongoDB,如何选择数据库、表,进行查询的操作。
package cn.itcast.mongodb;
import com.mongodb.client.*;
import org.bson.Document;
import java.util.function.Consumer;
public class MongoDBDemo {
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {
<span class="hljs-comment">// 建立连接</span>
<span class="hljs-type">MongoClient</span> <span class="hljs-variable">mongoClient</span> <span class="hljs-operator">=</span>
MongoClients.create(<span class="hljs-string">"mongodb://192.168.136.160:27017"</span>);
<span class="hljs-comment">// 选择数据库</span>
<span class="hljs-type">MongoDatabase</span> <span class="hljs-variable">mongoDatabase</span> <span class="hljs-operator">=</span> mongoClient.getDatabase(<span class="hljs-string">"testdb"</span>);
<span class="hljs-comment">// 选择表</span>
MongoCollection<Document> userCollection = mongoDatabase.getCollection(<span class="hljs-string">"person"</span>);
<span class="hljs-comment">// 查询数据</span>
userCollection.find().limit(<span class="hljs-number">10</span>).forEach((Consumer<? <span class="hljs-built_in">super</span> Document>) document -> {
System.out.println(document.toJson());
});
<span class="hljs-comment">// 关闭连接</span>
mongoClient.close();
}
}
1.3.3. CURD 操作
public class TestCRUD {
<span class="hljs-keyword">private</span> MongoCollection<Document> mongoCollection;
<span class="hljs-meta">@Before</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">init</span><span class="hljs-params">()</span> {
<span class="hljs-comment">// 建立连接</span>
<span class="hljs-type">MongoClient</span> <span class="hljs-variable">mongoClient</span> <span class="hljs-operator">=</span>
MongoClients.create(<span class="hljs-string">"mongodb://192.168.136.160:27017"</span>);
<span class="hljs-comment">// 选择数据库</span>
<span class="hljs-type">MongoDatabase</span> <span class="hljs-variable">mongoDatabase</span> <span class="hljs-operator">=</span> mongoClient.getDatabase(<span class="hljs-string">"testdb"</span>);
<span class="hljs-comment">// 选择表</span>
<span class="hljs-built_in">this</span>.mongoCollection = mongoDatabase.getCollection(<span class="hljs-string">"person"</span>);
}
<span class="hljs-meta">@Test</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">insert</span><span class="hljs-params">()</span> {
<span class="hljs-type">Document</span> <span class="hljs-variable">document</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Document</span>()
.append(<span class="hljs-string">"id"</span>, <span class="hljs-number">10001</span>)
.append(<span class="hljs-string">"name"</span>, <span class="hljs-string">"zhangsan"</span>)
.append(<span class="hljs-string">"address"</span>, <span class="hljs-string">"北京顺义"</span>)
.append(<span class="hljs-string">"age"</span>, <span class="hljs-number">20</span>);
mongoCollection.insertOne(document);
}
<span class="hljs-meta">@Test</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">find</span><span class="hljs-params">()</span> {
FindIterable<Document> documents = mongoCollection.find();
documents.forEach((Consumer<? <span class="hljs-built_in">super</span> Document>) document -> {
System.out.println(document);
});
}
<span class="hljs-comment">//根据条件查询 age < 13 or age > 40</span>
<span class="hljs-meta">@Test</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">findByAge</span><span class="hljs-params">()</span> {
FindIterable<Document> documents = mongoCollection.find(Filters.or(Filters.lt(<span class="hljs-string">"age"</span>,<span class="hljs-number">13</span>),Filters.gt(<span class="hljs-string">"age"</span>,<span class="hljs-number">40</span>)));
documents.forEach((Consumer<? <span class="hljs-built_in">super</span> Document>) document -> {
System.out.println(document);
});
}
<span class="hljs-meta">@Test</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">update</span><span class="hljs-params">()</span> {
mongoCollection.updateOne(Filters.eq(<span class="hljs-string">"id"</span>,<span class="hljs-number">10001</span>), Updates.set(<span class="hljs-string">"age"</span>, <span class="hljs-number">12</span>));
}
<span class="hljs-meta">@Test</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">delete</span><span class="hljs-params">()</span> {
mongoCollection.deleteOne(Filters.eq(<span class="hljs-string">"id"</span>,<span class="hljs-number">10001</span>));
}
}
【小结】
1:了解 MongoDB
3:了解 MongoDB 基础操作
4:了解通过 JavaApi 操作 MongoDB
2. SpringBoot 整合 MongoDB(重点)
【目标】
了解 spring-data-mongdb
学会使用 spring-data-mongdb
【路径】
1:spring-data-mongdb 简介
2:spring-data-mongdb 使用
【讲解】
spring-data 对 MongoDB 做了支持,使用 spring-data-mongodb 可以简化 MongoDB 的操作,封装了底层的 mongodb-driver。
地址:https://spring.io/projects/spring-data-mongodb
使用 Spring-Data-MongoDB 很简单,只需要如下几步即可:
- 导入起步依赖
- 编写配置信息
- 编写实体类
- 创建启动类
- 注入 MongoTemplate 对象,完成 CRUD 操作
2.1. 环境搭建
2.2.1. 导入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
</dependency>
</dependencies>
2.2.2. 编写 application.yml 配置文件
spring:
data:
mongodb:
uri: mongodb://192.168.136.160:27017/test
2.2.3. 编写启动类
package com.tanhua.mongo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MongoApplication {
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {
SpringApplication.run(MongoApplication.class, args);
}
}
2.2. 完成基本操作
2.2.1. 编写实体类
package com.tanhua.mongo.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.core.mapping.Document;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(value="person")
public class Person {
<span class="hljs-keyword">private</span> ObjectId id;
<span class="hljs-keyword">private</span> String name;
<span class="hljs-keyword">private</span> <span class="hljs-type">int</span> age;
<span class="hljs-keyword">private</span> String address;
}
2.2.1. 完成 CRUD
package com.tanhua.mongo;
import com.tanhua.mongo.domain.Person;
import org.bson.types.ObjectId;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
/**
-
SpringBoot 整合 MongoDB 入门案例 - 重点掌握
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class MongoDBTest {
@Autowired
private MongoTemplate mongoTemplate;
/**
-
新增
*/
@Test
public void testInsert(){
Person person = new Person();
person.setName("隔壁老王");
person.setAge(18);
person.setAddress("隔壁");
mongoTemplate.insert(person);
// 操作 A
Person person2 = new Person();
person2.setName("隔壁老王 66");
person2.setAge(186);
person2.setAddress("隔壁 6");
person2.setId(ObjectId.get());
mongoTemplate.save(person2);
// 操作 B 要使用 A 主键 id
System.out.println(person2.getId());
}
/**
- 查询
*/
@Test
public void testFindAll(){
Query query = new Query();
List<Person> personList = mongoTemplate.find(query, Person.class);
for (Person person : personList) {
System.out.println(person);
}
}
/**
- 修改
*/
@Test
public void testupdate(){
//Query query: 更新条件 , Update update:更新的数据, Class<?> entityClass:操作实体对象
Query query = new Query();
//query.addCriteria(Criteria.where("_id").is(new ObjectId("617a0864b976e10ee3be3c6c")));// 条件构造方法
query.addCriteria(Criteria.where("age").is(968));// 条件构造方法
Update update = new Update();
update.set("username","隔壁老王 968");
//update.inc("xxxx",-10);// 针对某一个字段 进行增长 点赞字段 0 1
mongoTemplate.updateFirst(query,update,Person.class);
}
/**
- 删除
*/
@Test
public void testDel(){
Query query = new Query();
query.addCriteria(Criteria.where("age").is(968));
mongoTemplate.remove(query,Person.class);
}
/**
- 查询 - 分页 - 带条件
- db.person.find({age:{$gt:100}}).limit(10).skip(0)
*/
@Test
public void testFindPage(){
int pagesize =10;
int page = 2;
//1. 查询总记录数
Query query = new Query();
query.addCriteria(Criteria.where("age").gt(100));
query.limit(pagesize);
query.skip((page-1)*pagesize);
long count = mongoTemplate.count(query, Person.class);
//2,查询当前页面数据
List<Person> personList = mongoTemplate.find(query, Person.class);
//3. 封装 PageResult 对象返回
//pages // 总页数
long pages = count/pagesize + (count%pagesize > 0 ?1:0);
System.out.println("counts::::"+count+"::::pagesize:::::"+pagesize+"::::pages::::"+pages+"::::page:::::"+page+"::::items:::::"+personList.toString());
}
}
【小结】
掌握 spring-data-mongdb
3. 今日佳人
【目标】
今日佳人需求介绍
今日佳人功能分析
今日佳人功能实现
【路径】
1:了解今日佳人需求
2:今日佳人功能分析
3:今日佳人功能实现
【讲解】
在用户登录成功后,就会进入首页,首页中有今日佳人、推荐好友、探花、搜附近等功能。
今日佳人,会推荐缘分值最大的用户,进行展现出来。缘分值的计算是由用户的行为进行打分,如:点击、点赞、评论、学历、婚姻状态等信息组合而成的。
实现:我们先不考虑推荐的逻辑,假设现在已经有推荐的结果,我们只需要从结果中查询到缘分值最高的用户就可以了。
流程:
3.1. 表结构设计
#表结构 recommend_user 推荐用户表
{
"userId":1001, #推荐的用户id
"toUserId":1002, #用户id
"score":90, #推荐得分
"date":"2019/1/1" #日期
}
在 MongoDB 中只存储用户的 id 数据,其他的数据需要通过接口查询。
// 构造一些测试数据
use tanhua
// 存储函数
db.system.js.save({_id:"addRecommendUser",value:function () {
// 给 toUserId=1 的插入 98 条记录
for (var i = 2; i <= 100; i++) {
db.recommend_user.insert({
"userId":NumberInt(i), "toUserId":NumberInt(1), "score":((60 + Math.random() * 40).toFixed(2)) * 1, "date":"2020-10-21"
});
}
}});
// 调用函数
db.loadServerScripts();
addRecommendUser();
// 创建索引,toUserId:正序,score:倒序
db.recommend_user.createIndex({'toUserId':1,'score':-1})
3.2. 服务消费者 - 今日佳人
3.2.1. 接口说明
3.2.2. TodayBestVo
在 tanhua-domain 模块下创建 TodayBestVo
package com.tanhua.domain.vo;
import lombok.Data;
import java.io.Serializable;
@Data
public class TodayBestVo implements Serializable {
private Long id;
private String avatar;
private String nickname;
private String gender; // 性别 man woman
private Integer age;
private String[] tags;
private Long fateValue; // 缘分值
}
3.2.3. TodayBestController
在 tanhua-server 模块下创建 TodayBestController
package com.tanhua.server.controller;
import com.tanhua.domain.vo.TodayBestVo;
import com.tanhua.server.service.TodayBestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
佳人用户信息管理控制层
*/
public class TodayBestController {
private TodayBestService todayBestService;/**
今日佳人
*/
public ResponseEntity todayBest(){
TodayBestVo todayBestVo = todayBestService.todayBest();
return ResponseEntity.ok(todayBestVo);
}
}
3.2.4. TodayBestService
在 tanhua-server 模块下创建 TodayBestService
package com.tanhua.server.service;
import com.tanhua.domain.db.UserInfo;
import com.tanhua.domain.mongo.RecommendUser;
import com.tanhua.domain.vo.TodayBestVo;
import com.tanhua.dubbo.api.db.UserInfoApi;
import com.tanhua.dubbo.api.mongo.RecommendUserApi;
import com.tanhua.server.interceptor.UserHolder;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.List;
/**
-
佳人管理业务处理层
*/
@Service
public class TodayBestService {
@Reference
private RecommendUserApi recommendUserApi;
@Reference
private UserInfoApi userInfoApi;
/**
- 今日佳人
*/
public TodayBestVo todayBest() {
Long toUserId = UserHolder.getUserId();// 当前登录用户 id
//1. 查询 mongodb 获取今日佳人用户
RecommendUser recommendUser = recommendUserApi.queryWithMaxScore(toUserId);
//2. 如果没有查询到今日佳人 设置默认推荐用户
if(recommendUser == null){
recommendUser = new RecommendUser();
recommendUser.setUserId(20l);// 佳人用户 id
recommendUser.setToUserId(toUserId);// 当前用户 id
recommendUser.setScore(99d);// 缘分值
}
//3. 查询推荐用户的用户信息 tb_user_info
UserInfo userInfo = userInfoApi.findUserInfoByUserId(recommendUser.getUserId());
//4. 构建 vo 返回
TodayBestVo vo = new TodayBestVo();
BeanUtils.copyProperties(userInfo,vo);//nickname avatar gender age
if(!StringUtils.isEmpty(userInfo.getTags())){
vo.setTags(userInfo.getTags().split(","));
}
vo.setId(recommendUser.getUserId());// 佳人用户 id
vo.setFateValue(recommendUser.getScore().longValue());// 缘分值
return vo;
}
}
3.3. 服务提供者 - 今日佳人
3.3.1. 导入依赖
tanhua-domain 模块的 pom.xml 引入 mongo 依赖
<!--SpringDataMongo 起步依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
3.3.2. RecommendUser
在 tanhua-domain 模块下创建 RecommendUser
package com.tanhua.domain.mongo;
import lombok.Data;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
import java.io.Serializable;
@Data
@Document(collection = "recommend_user")
public class RecommendUser implements Serializable {
<span class="hljs-meta">@Id</span>
<span class="hljs-keyword">private</span> ObjectId id; <span class="hljs-comment">//主键id</span>
<span class="hljs-meta">@Indexed</span>
<span class="hljs-keyword">private</span> Long userId; <span class="hljs-comment">//推荐的用户id</span>
<span class="hljs-keyword">private</span> Long toUserId; <span class="hljs-comment">//登录用户id</span>
<span class="hljs-meta">@Indexed</span>
<span class="hljs-keyword">private</span> <span class="hljs-type">Double</span> <span class="hljs-variable">score</span> <span class="hljs-operator">=</span><span class="hljs-number">0d</span>; <span class="hljs-comment">//推荐得分</span>
<span class="hljs-keyword">private</span> String date; <span class="hljs-comment">//日期</span>
}
3.3.3. RecommendUserApi
在 tanhua-dubbo-interface 模块 mongo 包下创建 RecommendUserApi
package com.tanhua.dubbo.api.mongo;
import com.tanhua.domain.mongo.RecommendUser;
/**
佳人服务接口
/
public interface RecommendUserApi {
/*- 查询今日佳人
- @param toUserId
@return
*/
RecommendUser queryWithMaxScore(Long toUserId);
}
3.3.4. RecommendUserApiImpl
在 tanhua-dubbo-service 模块 mongo 包创建 RecommendUserApiImpl
package com.tanhua.dubbo.api.mongo;
import com.tanhua.domain.mongo.RecommendUser;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
/**
佳人服务接口实现类
*/
public class RecommendUserApiImpl implements RecommendUserApi{
private MongoTemplate mongoTemplate;/**
- 查询今日佳人
- @param toUserId
@return
*/
public RecommendUser queryWithMaxScore(Long toUserId) {
Query query = new Query();// 构造条件 toUserId score 降序 date== 当天时间(省略)
query.addCriteria(Criteria.where("toUserId").is(toUserId));
query.with(Sort.by(Sort.Direction.DESC,"score"));
return mongoTemplate.findOne(query,RecommendUser.class);
}
}
3.3.5. application.yml
在 tanhua-dubbo-service 模块下
data:
mongodb:
uri: mongodb://10.10.20.160:27017/tanhua
3.3.6. 服务消费者启动 bug
在项目中,添加了 mongo 的依赖的话,springboot 就会自动去连接本地的 mongo,由于他连接不上会导致出错。
解决方案:
-
springboot 中添加排除自动配置的注解
package com.tanhua.server;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
@SpringBootApplication(exclude = {
MongoAutoConfiguration.class
}) // 排除 mongo 的自动配置
public class TanhuaServerApplication {
public static void main(String[] args) {
SpringApplication.run(TanhuaServerApplication.class,args);
}
}
3.3.7. 测试
postman 测试
预期效果
【小结】
掌握今日佳人功能
作业
提前熟悉下 day05 圈子相关表以及表之间关系
总结
-
理解 MongoDB 基本概念(体系结构 数据类型 )
-
MongoDB 命令操作 - 掌握
查询:db. 集合名称.find({条件})
删除:db. 集合名称.remove({条件})
更新:db. 集合名称.update({条件},{需要更新的数据})
索引:如何创建查询索引 看执行计划(解释计划)
-
SpringBoot 整合 MongoDB(重点)
创建 springboot 整合 mongodb 工程 引入相关依赖(mongodb 起步依赖 等)
创建 application.yml mongodb 配置
创建实体对象对象(@Document 注解建立跟表映射关系)
创建启动类
创建测试类 @Autowried MongoTemplate insert、remove 、 updateFirst 、find findOne findAll 分页
-
今日佳人
业务流程:
1. 大数据推荐系统 ==> Mongodb 数据库 ==> RecommendUser 表中 (userId toUserId socre) 写入数据
2. 先从 RecommendUser 查询佳人信息(1 条记录)
3. 如果 RecommendUser 表数据为空则设置默认数据
4. 根据 userId 查询 tb_user_info 表
5. 将 score 和 userInfo 数据封装 Vo 返回
注意:
1.tanhua_domain 工程中加入 mongodb 起步依赖
2.tanhua_service 工程中 application.yml 加入 mongodb 配置
3.tanhua_server 工程中启动类将 MongoAutoConfiguration 排除