1. 通用设置
MongoDB环境搭建
MongoDB基本CRUD操作
通过JavaApi操作MongoDB
SpringBoot整合MongoDB
1.1 需求分析 1.1.1 需求分析 通用设置,包含探花交友APP基本的软件设置功能。包含:
设置陌生人问题:当平台其他用户想进行在线交流时需要回答陌生人问题。
通用设置:包含一些APP通知设置
黑名单:对于不感兴趣的用户设置黑名单屏蔽骚扰
1.1.2 数据库表 通用设置
1 2 3 4 5 6 7 8 9 10 CREATE TABLE `tb_settings` ( `id` bigint (20 ) NOT NULL AUTO_INCREMENT, `user_id` bigint (20 ) DEFAULT NULL , `like_notification` tinyint(4 ) DEFAULT '1' COMMENT '推送喜欢通知' , `pinglun_notification` tinyint(4 ) DEFAULT '1' COMMENT '推送评论通知' , `gonggao_notification` tinyint(4 ) DEFAULT '1' COMMENT '推送公告通知' , `created` datetime DEFAULT NULL , `updated` datetime DEFAULT NULL , PRIMARY KEY (`id`) ) ENGINE= InnoDB DEFAULT CHARSET= utf8 COMMENT= '设置表' ;
问题表
1 2 3 4 5 6 7 8 9 CREATE TABLE `tb_question` ( `id` bigint (20 ) NOT NULL AUTO_INCREMENT, `user_id` bigint (20 ) DEFAULT NULL COMMENT '用户id' , `txt` varchar (200 ) DEFAULT NULL COMMENT '问题内容' , `created` datetime DEFAULT NULL , `updated` datetime DEFAULT NULL , PRIMARY KEY (`id`), KEY `user_id` (`user_id`) ) ENGINE= InnoDB AUTO_INCREMENT= 6 DEFAULT CHARSET= utf8
黑名单
1 2 3 4 5 6 7 8 9 CREATE TABLE `tb_black_list` ( `id` bigint (20 ) NOT NULL AUTO_INCREMENT, `user_id` bigint (20 ) DEFAULT NULL , `black_user_id` bigint (20 ) DEFAULT NULL , `created` datetime DEFAULT NULL , `updated` datetime DEFAULT NULL , PRIMARY KEY (`id`), KEY `user_id` (`user_id`) ) ENGINE= InnoDB DEFAULT CHARSET= utf8 COMMENT= '黑名单' ;
1.1.3 实体类 Settings 1 2 3 4 5 6 7 8 9 10 11 12 @Data @NoArgsConstructor @AllArgsConstructor public class Settings extends BasePojo { private Long id; private Long userId; private Boolean likeNotification; private Boolean pinglunNotification; private Boolean gonggaoNotification; }
Question 1 2 3 4 5 6 7 8 9 10 11 @Data @NoArgsConstructor @AllArgsConstructor public class Question extends BasePojo { private Long id; private Long userId; private String txt; }
BlackList 1 2 3 4 5 6 7 8 9 @Data @NoArgsConstructor @AllArgsConstructor public class BlackList extends BasePojo { private Long id; private Long userId; private Long blackUserId; }
1.2 查询通用设置 1.2.1 接口文档 接口地址:http://192.168.136.160:3000/project/19/interface/api/268
1.2.2 代码实现 vo对象 1 2 3 4 5 6 7 8 9 10 11 12 13 @Data @NoArgsConstructor @AllArgsConstructor public class SettingsVo implements Serializable { private Long id; private String strangerQuestion = "" ; private String phone; private Boolean likeNotification = true ; private Boolean pinglunNotification = true ; private Boolean gonggaoNotification = true ; }
tanhua-app-server 在tanhua-app-server
中创建SettingsController
和SettingsService
SettingsController 在SettingsController
中定义控制器接收请求 , 调用业务层完成业务逻辑
1 2 3 4 5 6 7 8 @GetMapping("/settings") public ResponseEntity settings () { SettingsVo vo = settingsService.settings(); return ResponseEntity.ok(vo); }
SettingsService 在tanhua-server
工程创建SettingsService
完成代码编写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package com.tanhua.server.service;import com.baomidou.mybatisplus.core.metadata.IPage;import com.tanhua.dubbo.api.BlackListApi;import com.tanhua.dubbo.api.QuestionApi;import com.tanhua.dubbo.api.SettingsApi;import com.tanhua.model.domain.Question;import com.tanhua.model.domain.Settings;import com.tanhua.model.domain.UserInfo;import com.tanhua.model.vo.PageResult;import com.tanhua.model.vo.SettingsVo;import com.tanhua.server.interceptor.UserHolder;import org.apache.dubbo.config.annotation.DubboReference;import org.springframework.stereotype.Service;import java.util.Map;@Service public class SettingsService { @DubboReference private QuestionApi questionApi; @DubboReference private SettingsApi settingsApi; public SettingsVo settings () { SettingsVo vo = new SettingsVo(); Long userId = UserHolder.getUserId(); vo.setId(userId); vo.setPhone(UserHolder.getMobile()); Question question = questionApi.findByUserId(userId); String txt = question == null ? "你喜欢java吗?" : question.getTxt(); vo.setStrangerQuestion(txt); Settings settings = settingsApi.findByUserId(userId); if (settings != null ) { vo.setGonggaoNotification(settings.getGonggaoNotification()); vo.setPinglunNotification(settings.getPinglunNotification()); vo.setLikeNotification(settings.getLikeNotification()); } return vo; } }
tanhua-dubbo-interface 在tanhua-dubbo-interface
模块配置服务接口
SettingsApi 1 2 3 4 5 6 7 8 9 package com.tanhua.dubbo.api;import com.tanhua.model.domain.Settings;public interface SettingsApi { Settings findByUserId (Long userId) ; }
QuestionApi 1 2 3 4 5 6 7 8 9 package com.tanhua.dubbo.api;import com.tanhua.model.domain.Question;public interface QuestionApi { Question findByUserId (Long userId) ; }
tanhua-dubbo-db 在tanhua-dubbo-db**
模块配置服务实现类和数据访问层Mapper
数据访问层 SettingsMapper
1 2 3 4 5 6 7 8 9 10 package com.tanhua.dubbo.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.tanhua.model.domain.Settings;public interface SettingsMapper extends BaseMapper <Settings > {}
QuestionMapper
1 2 3 4 5 6 7 8 9 10 11 package com.tanhua.dubbo.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.tanhua.model.domain.Question;public interface QuestionMapper extends BaseMapper <Question > {}
服务实现类 SettingsApiImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.tanhua.dubbo.api;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.tanhua.dubbo.mappers.SettingsMapper;import com.tanhua.model.domain.Settings;import org.apache.dubbo.config.annotation.DubboService;import org.springframework.beans.factory.annotation.Autowired;@DubboService public class SettingsApiImpl implements SettingsApi { @Autowired private SettingsMapper settingsMapper; public Settings findByUserId (Long userId) { QueryWrapper<Settings> qw = new QueryWrapper<>(); qw.eq("user_id" ,userId); return settingsMapper.selectOne(qw); } }
QuestionApiImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.tanhua.dubbo.api;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.tanhua.dubbo.mappers.QuestionMapper;import com.tanhua.model.domain.Question;import org.apache.dubbo.config.annotation.DubboService;import org.springframework.beans.factory.annotation.Autowired;@DubboService public class QuestionApiImpl implements QuestionApi { @Autowired private QuestionMapper questionMapper; @Override public Question findByUserId (Long userId) { QueryWrapper<Question> qw = new QueryWrapper<>(); qw.eq("user_id" ,userId); return questionMapper.selectOne(qw); } }
1.3 设置陌生人问题
对数据库表进行操作:如果存在数据,更新数据库。如果不存在数据,保存数据库表数据
1.3.1 接口文档 接口地址:http://192.168.136.160:3000/project/19/interface/api/277
1.3.2 代码实现 tanhua-app-server 在tanhua-app-server
中SettingsController
和settingsService
添加方法
SettingsController 在SettingsController
中定义保存用户陌生人问题的方法
1 2 3 4 5 6 7 8 9 10 @PostMapping("/questions") public ResponseEntity questions (@RequestBody Map map) { String content = (String) map.get("content" ); settingsService.saveQuestion(content); return ResponseEntity.ok(null ); }
SettingsService 在SettingsService
中编写保存用户陌生人问题的业务逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public void saveQuestion (String content) { Long userId = UserHolder.getUserId(); Question question = questionApi.findByUserId(userId); if (question == null ) { question = new Question(); question.setUserId(userId); question.setTxt(content); questionApi.save(question); }else { question.setTxt(content); questionApi.update(question); } }
tanhua-dubbo-interface 在tanhua-dubbo-interface
模块的QuestionApi
中定义保存和更新陌生人问题的方法
1 2 3 4 5 void save (Question question) ;void update (Question question) ;
tanhua-dubbo-db 在tanhua-dubbo-db
模块的QuestionApiImpl
实现保存和更新陌生人问题的方法
1 2 3 4 5 6 7 8 9 public void save (Question question) { questionMapper.insert(question); } public void update (Question question) { questionMapper.updateById(question); }
1.4 通知设置 1.4.1 接口文档 通知管理:对通知进行保存或者更新的操作
http://192.168.136.160:3000/project/19/interface/api/280
1.4.2 代码实现 tanhua-app-server 在tanhua-app-server
中SettingsController
和settingsService
添加方法
SettingsController 在SettingsController
中定义保存用户通知设置的方法
1 2 3 4 5 6 7 8 9 @PostMapping("/notifications/setting") public ResponseEntity notifications (@RequestBody Map map) { settingsService.saveSettings(map); return ResponseEntity.ok(null ); }
settingsService 在SettingsService
中编写保存用户通知的业务逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public void saveSettings (Map map) { boolean likeNotification = (Boolean) map.get("likeNotification" ); boolean pinglunNotification = (Boolean) map.get("pinglunNotification" ); boolean gonggaoNotification = (Boolean) map.get("gonggaoNotification" ); Long userId = UserHolder.getUserId(); Settings settings = settingsApi.findByUserId(userId); if (settings == null ) { settings = new Settings(); settings.setUserId(userId); settings.setPinglunNotification(pinglunNotification); settings.setLikeNotification(likeNotification); settings.setGonggaoNotification(gonggaoNotification); settingsApi.save(settings); }else { settings.setPinglunNotification(pinglunNotification); settings.setLikeNotification(likeNotification); settings.setGonggaoNotification(gonggaoNotification); settingsApi.update(settings); } }
tanhua-dubbo-interface 在tanhua-dubbo-interface
模块的SettingsApi
中定义保存和更新通知设置的API接口方法
1 2 3 4 5 void save (Settings settings) ;void update (Settings settings) ;
tanhua-dubbo-db 在tanhua-dubbo-db
模块的SettingsApiImpl
中实现保存和更新通知设置的API接口方法
1 2 3 4 5 6 7 8 9 public void save (Settings settings) { settingsMapper.insert(settings); } public void update (Settings settings) { settingsMapper.updateById(settings); }
1.5 黑名单管理 1.5.1 查询黑名单列表 接口文档 接口地址:http://192.168.136.160:3000/project/19/interface/api/286
vo对象 在tanhua-model
工程中配置分页vo对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package com.tanhua.domain.vo;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import java.io.Serializable;import java.util.Collections;import java.util.List;@Data @AllArgsConstructor @NoArgsConstructor public class PageResult implements Serializable { private Integer counts = 0 ; private Integer pagesize; private Integer pages = 0 ; private Integer page; private List<?> items = Collections.emptyList(); public PageResult (Integer page,Integer pagesize,int counts,List list) { this .page = page; this .pagesize = pagesize; this .items = list; this .counts = counts; this .pages = counts % pagesize == 0 ? counts / pagesize : counts / pagesize + 1 ; } }
tanhua-app-server 在tanhua-app-server
中SettingsController
和settingsService
添加方法
SettingsController 在SettingsController
中定义查询用户黑名单列表的方法
1 2 3 4 5 6 7 8 9 10 11 12 @GetMapping("/blacklist") public ResponseEntity blacklist ( @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "10") int size) { PageResult pr = settingsService.blacklist(page,size); return ResponseEntity.ok(pr); }
settingsService 在SettingsService
中定义查询用户黑名单列表的方法 , 编写查询用户黑名单列表的业务逻辑
1 2 3 4 5 6 7 8 9 10 11 public PageResult blacklist (int page, int size) { Long userId = UserHolder.getUserId(); IPage<UserInfo> iPage = blackListApi.findByUserId(userId,page,size); PageResult pr = new PageResult(page,size,iPage.getTotal(),iPage.getRecords()); return pr; }
tanhua-dubbo-interface 在tanhua-dubbo-interface
模块的创建BlackListApi
定义查询用户黑名单列表的方法
1 2 3 4 5 6 7 8 9 10 package com.tanhua.dubbo.api;import com.baomidou.mybatisplus.core.metadata.IPage;import com.tanhua.model.domain.UserInfo;public interface BlackListApi { IPage<UserInfo> findByUserId (Long userId, int page, int size) ; }
tanhua-dubbo-db 在tanhua-dubbo-db
模块创建BlackListApiImpl
和Mapper接口
BlackListApiImpl 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package com.tanhua.dubbo.api;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.baomidou.mybatisplus.core.metadata.IPage;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;import com.tanhua.dubbo.mappers.BlackListMapper;import com.tanhua.dubbo.mappers.UserInfoMapper;import com.tanhua.model.domain.BlackList;import com.tanhua.model.domain.UserInfo;import org.apache.dubbo.config.annotation.DubboService;import org.springframework.beans.factory.annotation.Autowired;@DubboService public class BlackListApiImpl implements BlackListApi { @Autowired private UserInfoMapper userInfoMapper; @Autowired private BlackListMapper blackListMapper; @Override public IPage<UserInfo> findByUserId (Long userId, int page, int size) { Page pages = new Page(page,size); return userInfoMapper.findBlackList(pages,userId); } }
UserInfoMapper 在UserInfoMapper
中编写查询方法
1 2 3 4 5 6 7 public interface UserInfoMapper extends BaseMapper <UserInfo > { @Select("select * from tb_user_info where id in (\n" + " SELECT black_user_id FROM tb_black_list where user_id=#{userId}\n" + ")") IPage<UserInfo> findBlackList (@Param("pages") Page pages, @Param("userId") Long userId) ; }
使用xml映射文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="com.tanhua.dubbo.mappers.UserInfoMapper" > <resultMap id ="BaseResultMap" type ="com.tanhua.model.domain.UserInfo" > <id column ="id" property ="id" /> <result column ="nickname" property ="nickname" /> <result column ="birthday" property ="birthday" /> <result column ="avatar" property ="avatar" /> <result column ="gender" property ="gender" /> <result column ="age" property ="age" /> <result column ="city" property ="city" /> <result column ="income" property ="income" /> <result column ="education" property ="education" /> <result column ="profession" property ="profession" /> <result column ="marriage" property ="marriage" /> <result column ="tags" property ="tags" /> <result column ="coverPic" property ="coverPic" /> </resultMap > <select id ="findBlackList" resultMap ="BaseResultMap" > SELECT a.* from tb_user_info as a INNER JOIN tb_black_list as b on b.user_id = a.id where b.user_id = #{userId} </select > </mapper >
修改yaml文件:
1 2 3 4 5 6 7 8 9 10 11 12 mybatis-plus: mapper-locations: classpath*:/mapper/*Mapper.xml type-aliases-package: com.tanhua.model configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: table-prefix: tb_ id-type: auto
BlackListMapper 1 2 3 4 5 6 7 package com.tanhua.dubbo.mappers;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.tanhua.model.domain.BlackList;public interface BlackListMapper extends BaseMapper <BlackList > {}
配置分页插件 tanhua-dubbo-db
引导类开启mybatis-plus分页插件支持
1 2 3 4 5 6 @Bean public MybatisPlusInterceptor mybatisPlusInterceptor () { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; }
使用mybatis-plus的分页:
创建分页对象:Page,指定当前页和每页查询条数
基础查询:mapper.selectPage(page,查询条件)
自定义查询:Ipage 方法名称(Page对象,xxx查询条件)
测试:
1.5.2 移除黑名单 接口文档 接口地址:http://192.168.136.160:3000/project/19/interface/api/283
tanhua-app-server 在tanhua-app-server
中SettingsController
和settingsService
添加方法
SettingsController 在SettingsController
中定义控制方法接收请求
1 2 3 4 5 6 7 8 @DeleteMapping("/blacklist/{uid}") public ResponseEntity deleteBlackList (@PathVariable("uid") Long blackUserId) { settingsService.deleteBlackList(blackUserId); return ResponseEntity.ok(null ); }
settingsService 在SettingsService
中定义方法 , 完成移除黑名单业务逻辑编写
1 2 3 4 5 6 7 public void deleteBlackList (Long blackUserId) { Long userId = UserHolder.getUserId(); blackListApi.delete(userId,blackUserId); }
tanhua-dubbo-interface 在tanhua-dubbo-interface
模块的BlackListApi
中定义移除用户黑名单的方法
1 2 void delete (Long userId, Long blackUserId) ;
tanhua-dubbo-db 在tanhua-dubbo-db
模块的BlackListApi
中实现移除用户黑名单的方法
1 2 3 4 5 6 7 public void delete (Long userId, Long blackUserId) { QueryWrapper<BlackList> qw = new QueryWrapper<>(); qw.eq("user_id" ,userId); qw.eq("black_user_id" ,blackUserId); blackListMapper.delete(qw); }
2. MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点做分析:
数据量会随着用户数增大而增大
读多写少
价值较低
非好友看不到其动态内容
地理位置的查询
……
针对以上特点,我们来分析一下:
mysql:关系型数据库(效率低)
redis:redis缓存(微博,效率高,数据格式不丰富)
对于数据量大而言,显然不能够使用关系型数据库进行存储,我们需要通过MongoDB进行存储
对于读多写少的应用,需要减少读取的成本
比如说,一条SQL语句,单张表查询一定比多张表查询要快
探花交友
mongodb:存储业务数据(圈子,推荐的数据,小视频数据,点赞,评论等)
redis:承担的角色是缓存层(提升查询效率)
mysql:存储和核心业务数据,账户
2.1 MongoDB简介 MongoDB:是一个高效的非关系型数据库(不支持表关系:只能操作单表)
MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。
MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的,它支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。
MongoDB最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。
官网:https://www.mongodb.com
2.2 MongoDB的特点 MongoDB 最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。它是一个面向集合的,模式自由的文档型数据库。具体特点总结如下:
面向集合存储,易于存储对象类型的数据
模式自由
支持动态查询
支持完全索引,包含内部对象
支持复制和故障恢复
使用高效的二进制数据存储,包括大型对象(如视频等)
自动处理碎片,以支持云计算层次的扩展性
支持 Python,PHP,Ruby,Java,C,C#,Javascript,Perl及C++语言的驱动程 序, 社区中也提供了对Erlang及.NET 等平台的驱动程序
文件存储格式为 BSON(一种 JSON 的扩展)
MYSQL : 用于存储安全性要求比较高的数据
REDIS : 存储数据格式简单 , 并且查询非常多的数据(用户缓存)
MONGDB : 用户存储海量数据, 并且数据的安全性要求不高
Mongodb的使用场景:
特征:修改频次较高
特征:地理位置信息、海量的数据
特征:结构多变、数据量大
2.2.1 通过docker安装MongoDB doker拉取:
镜像下载:
1 2 3 4 5 6 # 进入base目录 cd /root/docker-file/base/ # 批量创建启动容器,其中已经包含了redis,zookeeper,mongodb容器 docker-compose up -d # 查看容器 docker ps -a
可以看到mongoDB已经启动,对外暴露了27017的操作端口
2.2.2 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字段设置为主键
2.3 数据类型
null:用于表示空值或者不存在的字段,{“x”:null}
布尔型:布尔类型有两个值true和false,{“x”:true}
数值:shell默认使用64为浮点型数值。{“x”:3.14}或{“x”:3}。对于整型值,可以使用 NumberInt(4字节符号整数)或NumberLong(8字节符号整数), {“x”:NumberInt(“3”)}{“x”:NumberLong(“3”)}
字符串:UTF-8字符串都可以表示为字符串类型的数据,{“x”:“呵呵”}
日期:日期被存储为自新纪元依赖经过的毫秒数,不存储时区,{“x”:new Date()}
正则表达式:查询时,使用正则表达式作为限定条件,语法与JavaScript的正则表达式相 同,{“x”:/[abc]/}
数组:数据列表或数据集可以表示为数组,{“x”: [“a“,“b”,”c”]}
内嵌文档:文档可以嵌套其他文档,被嵌套的文档作为值来处理,{“x”:{“y”:3 }}
对象Id:对象id是一个12字节的字符串,是文档的唯一标识,{“x”: objectId() }
二进制数据:二进制数据是一个任意字节的字符串。它不能直接在shell中使用。如果要 将非utf-字符保存到数据库中,二进制数据是唯一的方式。
3. MongoDB入门 3.1 数据库以及表的操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 # 查看所有的数据库 > show dbs # 通过use关键字切换数据库 > use admin # 创建数据库 # 说明:在MongoDB中,数据库是自动创建的,通过use切换到新数据库中,进行插入数据即可自动创建数据库 > use testdb > show dbs > db.user.insert({id:1,name:'zhangsan' }) > show dbs # 查看表 > show tables > show collections # 删除集合(表) > db.user.drop() true #如果成功删除选定集合,则 drop() 方法返回 true,否则返回 false。 # 删除数据库 > use testdb > db.dropDatabase()
3.2 新增数据 在MongoDB中,存储的文档结构是一种类似于json的结构,称之为bson(全称为:Binary JSON)。
1 2 3 4 5 6 7 # 插入数据 # 语法:db.表名.insert(json字符串) > db.user.insert({id:1,username:'zhangsan' ,age:20}) > db.user.find()
3.3 更新数据 update() 方法用于更新已存在的文档。语法格式如下:
1 2 3 4 5 6 7 8 9 db.collection.update( <query>, <update>, [ upsert: <boolean>, multi: <boolean>, writeConcern: <document> ] )
参数说明:
query : update的查询条件,类似sql update查询内where后面的。
update : update的对象和一些更新的操作符(如$,$inc.$set)等,也可以理解为sql update查询内set后面的
upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。
multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。
writeConcern :可选,抛出异常的级别。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # 查询全部 > 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 )
3.4 删除数据 通过remove()方法进行删除数据,语法如下:
1 2 3 4 5 6 7 db.collection.remove( <query>, { justOne: <boolean>, writeConcern: <document> } )
参数说明:
query :(可选)删除的文档的条件。
justOne : (可选)如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值 false,则删除所有匹配条件的文档。
writeConcern :(可选)抛出异常的级别。
实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 # 删除数据 > 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({})
3.5 查询数据 MongoDB 查询数据的语法格式如下:
1 db.user.find([query],[fields])
query :可选,使用查询操作符指定查询条件
fields :可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)。
条件查询:
操作
格式
范例
RDBMS中的类似语句
等于
{<key>:<value>
}
db.col.find({"by":"黑马程序员"}).pretty()
where by = '黑马程序员'
小于
{<key>:{$lt:<value>}}
db.col.find({"likes":{$lt:50}}).pretty()
where likes < 50
小于或等于
{<key>:{$lte:<value>}}
db.col.find({"likes":{$lte:50}}).pretty()
where likes <= 50
大于
{<key>:{$gt:<value>}}
db.col.find({"likes":{$gt:50}}).pretty()
where likes > 50
大于或等于
{<key>:{$gte:<value>}}
db.col.find({"likes":{$gte:50}}).pretty()
where likes >= 50
不等于
{<key>:{$ne:<value>}}
db.col.find({"likes":{$ne:50}}).pretty()
where likes != 50
实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # 插入测试数据 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({$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}) #按照id倒序排序,-1为倒序,1为正序
3.6 索引
索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。
这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。
索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # 创建索引 > db.user.createIndex({'age' :1}) # 查看索引 > db.user.getIndexes() [ { "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "testdb.user" } ] # 说明:1表示升序创建索引,-1表示降序创建索引。
3.7 执行计划 MongoDB 查询分析可以确保我们建议的索引是否有效,是查询语句性能分析的重要工具。
1 2 3 4 5 6 7 8 9 10 11 # 插入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" ,
4. SpringData-Mongo
Spring-data对MongoDB做了支持,使用spring-data-mongodb可以简化MongoDB的操作,封装了底层的mongodb-driver。
地址:https://spring.io/projects/spring-data-mongodb
使用Spring-Data-MongoDB很简单,只需要如下几步即可:
导入起步依赖
编写配置信息
编写实体类(配置注解 @Document,@Id)
操作mongodb
注入MongoTemplate对象,完成CRUD操作
编写Repository接口,注入接口完成基本Crud操作
4.1 环境搭建
第一步,导入依赖:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 2.3.9.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 > </dependencies >
第二步,编写application.yml配置文件
1 2 3 4 spring : data : mongodb : uri : mongo://192.168.136.160:27017
第三步,编写启动类
1 2 3 4 5 6 7 8 9 10 11 12 package com.tanhua.mongo;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication public class MongoApplication { public static void main (String[] args) { SpringApplication.run(MongoApplication.class, args); } }
4.2 完成基本操作
第一步,编写实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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 { @Id private ObjectId id; @Field("username") private String name; private int age; private String address; }
第二步,通过MongoTemplate完成CRUD操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 package cn.itcast.mongo.test;import cn.itcast.mongo.MongoApplication;import cn.itcast.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;@RunWith(SpringRunner.class) @SpringBootTest(classes = MongoApplication.class) public class MongoTest { @Autowired private MongoTemplate mongoTemplate; @Test public void testSave () { for (int i = 0 ; i < 10 ; i++) { Person person = new Person(); person.setId(ObjectId.get()); person.setName("张三" +i); person.setAddress("北京顺义" +i); person.setAge(18 +i); mongoTemplate.save(person); } } @Test public void testFindAll () { List<Person> list = mongoTemplate.findAll(Person.class); for (Person person : list) { System.out.println(person); } } @Test public void testFind () { Query query = new Query(Criteria.where("age" ).lt(20 )); List<Person> list = mongoTemplate.find(query, Person.class); for (Person person : list) { System.out.println(person); } } @Test public void testPage () { Criteria criteria = Criteria.where("age" ).lt(30 ); Query queryCount = new Query(criteria); long count = mongoTemplate.count(queryCount, Person.class); System.out.println(count); Query queryLimit = new Query(criteria) .limit(2 ) .skip(2 ) ; List<Person> list = mongoTemplate.find(queryLimit, Person.class); for (Person person : list) { System.out.println(person); } } @Test public void testUpdate () { Query query = Query.query(Criteria.where("id" ).is("5fe404c26a787e3b50d8d5ad" )); Update update = new Update(); update.set("age" , 20 ); mongoTemplate.updateFirst(query, update, Person.class); } @Test public void testRemove () { Query query = Query.query(Criteria.where("id" ).is("5fe404c26a787e3b50d8d5ad" )); mongoTemplate.remove(query, Person.class); } }
5. 今日佳人 在用户登录成功后,就会进入首页,首页中有今日佳人 推荐好友 探花 搜附近等功能。
我们先不考虑推荐的逻辑,假设现在已经有推荐的结果,我们只需要从结果中查询到缘分值最高的用户就可以了
5.1 表结构设计 1 2 3 4 5 6 7 # 表结构 recommend_user { "userId":1001, #推荐的用户id "toUserId":1002, #用户id "score":90, #推荐得分 "date":"2019/1/1" #日期 }
在MongoDB中只存储用户的id数据,其他的数据需要通过接口查询。
5.2 服务提供者 在tanhua-dubbo-mongo
模块编写服务提供者相应代码
5.2.0 导入依赖 找到tanhua-model
模块的pom.xml
添加mongo的依赖
1 2 3 4 5 6 7 8 9 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-data-mongodb</artifactId > </dependency > <dependency > <groupId > org.mongodb</groupId > <artifactId > mongodb-driver-sync</artifactId > </dependency >
5.2.1 实体类 找到tanhua-model
模块编写实体类com.tanhua.model.mongo.RecommendUser
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.tanhua.model.mongo;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import org.bson.types.ObjectId;import org.springframework.data.mongodb.core.mapping.Document;@AllArgsConstructor @NoArgsConstructor @Data @Document(collection = "recommend_user") public class RecommendUser implements java .io .Serializable { private ObjectId id; private Long userId; private Long toUserId; private Double score =0d ; private String date; }
5.2.2 RecommendUserApi接口 在tanhua-dubbo-interface
模块创建公共接口com.tanhua.dubbo.api.RecommendUserApi
1 2 3 4 5 6 7 8 9 10 11 package com.tanhua.dubbo.api;import com.tanhua.model.mongo.RecommendUser;import com.tanhua.model.vo.PageResult;import java.util.List;public interface RecommendUserApi { RecommendUser queryWithMaxScore (Long toUserId) ; }
5.2.3 RecommendUserApiImpl 在tanhua-dubbo-mongo
模块创建接口实现类com.tanhua.dubbo.api.RecommendUserApiImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package com.tanhua.dubbo.api;import cn.hutool.core.collection.CollUtil;import com.tanhua.model.mongo.RecommendUser;import com.tanhua.model.mongo.UserLike;import com.tanhua.model.vo.PageResult;import org.apache.dubbo.config.annotation.DubboService;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.aggregation.Aggregation;import org.springframework.data.mongodb.core.aggregation.AggregationResults;import org.springframework.data.mongodb.core.aggregation.TypedAggregation;import org.springframework.data.mongodb.core.query.Criteria;import org.springframework.data.mongodb.core.query.Query;import java.util.List;@DubboService public class RecommendUserApiImpl implements RecommendUserApi { @Autowired private MongoTemplate mongoTemplate; public RecommendUser queryWithMaxScore (Long toUserId) { Criteria criteria = Criteria.where("toUserId" ).is(toUserId); Query query = Query.query(criteria).with(Sort.by(Sort.Order.desc("score" ))) .limit(1 ); return mongoTemplate.findOne(query,RecommendUser.class); } }
5.2.4 application配置 在tanhua-dubbo-mongo
模块添加配置文件application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 server: port: 18082 spring: application: name: tanhua-dubbo-mongo cloud: nacos: discovery: server-addr: 192.168 .136 .160 :8848 data: mongodb: uri: mongodb://192.168.136.160:27017/tanhua dubbo: protocol: name: dubbo port: 20881 registry: address: spring-cloud://localhost scan: base-packages: com.tanhua.dubbo.api
5.2.5 启动类 在tanhua-dubbo-mongo
模块添加启动类com.tanhua.dubbo.DubboMongoApplication
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.tanhua.dubbo;import com.baomidou.mybatisplus.annotation.DbType;import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.annotation.Bean;@SpringBootApplication public class DubboMongoApplication { public static void main (String[] args) { SpringApplication.run(DubboMongoApplication.class,args); } }
5.3 代码实现 5.3.1 接口说明 地址:http://192.168.136.160:3000/project/19/interface/api/100
5.3.2 TanhuaController 在tanhua-app-server
模块创建TanhuaController
并编写代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @RestController @RequestMapping("/tanhua") public class TanhuaController { @Autowired private TanhuaService tanhuaService; @GetMapping("/todayBest") public ResponseEntity todayBest () { TodayBest vo = tanhuaService.todayBest(); return ResponseEntity.ok(vo); } }
5.3.3 TanhuaService 在tanhua-app-server
模块创建TanhuaService并编写代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 @Service public class TanhuaService { @DubboReference private RecommendUserApi recommendUserApi; @DubboReference private UserInfoApi userInfoApi; @DubboReference private QuestionApi questionApi; @Autowired private HuanXinTemplate template; public TodayBest todayBest () { Long userId = UserHolder.getUserId(); RecommendUser recommendUser = recommendUserApi.queryWithMaxScore(userId); if (recommendUser == null ) { recommendUser = new RecommendUser(); recommendUser.setUserId(1l ); recommendUser.setScore(99d ); } UserInfo userInfo = userInfoApi.findById(recommendUser.getUserId()); TodayBest vo = TodayBest.init(userInfo, recommendUser); return vo; } }
5.3.4 vo对象 在tanhua-model
模块创建VO对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package com.tanhua.domain.vo;import com.tanhua.domain.db.UserInfo;import com.tanhua.domain.mongo.RecommendUser;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import org.springframework.beans.BeanUtils;@Data @NoArgsConstructor @AllArgsConstructor public class TodayBest { private Long id; private String avatar; private String nickname; private String gender; private Integer age; private String[] tags; private Long fateValue; public static TodayBest init (UserInfo userInfo, RecommendUser recommendUser) { TodayBest vo = new TodayBest(); BeanUtils.copyProperties(userInfo,vo); if (userInfo.getTags() != null ) { vo.setTags(userInfo.getTags().split("," )); } vo.setFateValue(recommendUser.getScore().longValue()); return vo; } }
5.3.5 启动报错 当我们在项目中引入了MongoDB依赖之后启动tanhua-dubbo-db项目和tanhua-app-server项目
时会报如下错误
原因 : 实体类模块中引入了MongoDB的依赖,根据自动装配的原理 tanhua-dubbo-db
和tanhua-app-server
中会自动查找默认MongoDB的地址(localhost:27017),而本地有没有开启Mongo所以连接失败。
解决方案 : tanhua-dubbo-db
和tanhua-app-server
中排除掉MongoDB 自动配置类即可
1 2 3 4 5 6 @SpringBootApplication(exclude = { MongoAutoConfiguration.class, MongoDataAutoConfiguration.class }) @MapperScan("com.tanhua.dubbo.mapper") public class DubboDbApplication {
}
```
**同理 : **在tanhua-app-server
项目中排除MongoDB 自动配置类即可