mongodb评论功能实现

mongodb评论功能实现

对应的项目在我的下载里

一、mongodb 的优势与劣势

特点:在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

MongoDB目前3大核心优势:『灵活模式』+ 『高可用性』 + 『可扩展性』,通过json文档来实现灵活模式,通过复制集来保证高可用,通过Sharded cluster来保证可扩展性。

应用场景:

  1. 业务需要事务,使用mysql,因为mongodb不支持事务
  2. 数据量大,但是数据本身价值不大,使用mongodb
    ps:加载大量低价值的业务数据
  3. 数据是非结构化的,且数据量大,使用mongodb
  4. 业务未来走向不明确,使用mongodb,方便扩展

优势:

  • 不存在SQL注入
  • 不需要提前创建表,可以直接写入数据
  • 字段数据格式自由 比如mysql中id 字段是数字,输入字符串会出错,mongodb不会
  • 可以处理json结构,并对其进行处理
    比如 字段a的值为
    {“a”:11,“b”:12,“c”:“abc”,“d”:[1,2,3]}
    你可以直接去读取或设置a字段的b值 a.b,读取a字段d数组的第二个值:a.d.1,可以去删除a字段的a数据$unset:{“a.a”:1},就会变成:
    {“b”:12,“c”:“abc”,“d”:[1,2,3]}
  • 可以使用upsert操作,修改的数据不存在的时候直接插入该数据
  • 查询和插入效率在没有索引的情况下远大于mysql

缺点:

  • mongodb是nosql数据库,关系能力薄弱,不能使用join,union来联合查找
  • 事务能力薄弱
  • 效率存在波动性,不是很稳定

MongoDB可以将模式设计划分为内嵌模式和 引用模式

内嵌模式

简单来讲,内嵌模式就是将关联数据,放在一个文档中。例如以下员工信息采用内嵌模式了而存储在了一个文档中:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OOSo6Eoo-1599922848721)(img\mongo01.png)]

根据上面的描述可以看出,内嵌模型可以给应用程序提供很好的数据查询性能,因为基于内嵌模型,可以通过一次数据库操作得到所有相关的数据。同时,内嵌模型可以使数据更新操作变成一个原子写操作。然而,内嵌模型也可能引入一些问题,比如说文档会越来越大,这样就可能会影响数据库写操作的性能,还可能会产生数据碎片

我们下面的demo就采用内嵌模型实现。

引用模式

引用模式是将数据存储在不同集合的文档中,而通过关系数据进行关联。例如,这里采用引用模式将员工信息存储在了3个文档中,基本信息一个文档,联系方式一个文档,登录权限放在了一个文档中。每个文档之前通过user_id来关联。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5P6Dw3Aj-1599922848724)(img\mongo02.png)]

使用内嵌模型往往会带来数据的冗余,却可以提升数据查询的效率。但是,当应用程序基本上不通过内嵌模型查询,或者说查询效率的提升不足以弥补数据冗余带来的问题时,我们就应该考虑引用模型了。

二、评论实现

1.数据库设计

(1)自由模式,无需提前声明、创建表结构,即不用先创建表、添加字段,然后才可以Insert数据。默认情况下MongoDB无需这样操作。

(2)键值类型自由,MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。字段值可以包含其他文档,数组及文档数组。

按照业务来慢慢添加,整个集合的存储文档格式就可以形成了。

一般现在最常见的和最实用的都是二级评论,所以这里用二级评论举例

一级评论, 文章id,用户id,用户评论内容,用户评论时间,评论回复

其中回复即二级评论,

包括 评论者id,评论者昵称,评论者评论内容,评论时间

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vP9eYbX3-1599922848727)(C:\Users\hasee\Desktop\mongodb实现评论\img\mongo03.png)]

这里就遇到一个问题,二级评论可不可以被评论,如果不可以,显然不合理。

所以这里仿照微博,在遇到二级评论的评论时,通过@确定回复的用户,内容全部和二级评论在同一级显示。

因此我们设计出这样一个数据结构(此时二级评论和二级评论的回复是不同级的)

{
    "_id" : ObjectId("597aa23add840cd4ce0681d1"),
    "comment_blog_id" : "ObjectId("597aa23add840213432rdsfsed1")",
    "comment_user_id" : "100001",
    "comment_user_name": "刘德宝",
    "comment_user_img": "图片地址",
    "comment_content" : "1号用户的评论",
    "create_time" : "2017-15-12 14:00",
    "comment_responses" : [ 
        {
        "_id" : ObjectId("597aa23add840cd4ce0681d1"),
            "response_user_id" : "1000002",
            "response_user_name" : "朱秀秀",
            "response_user_img" : "图片地址",
            "response_content" : [ 
                "这是2号用户给刘德宝的评论", 
            ],
            "create_time" : "2017-15-12 14:00",
        }, 
        {
            "response_user_id" : "1000005",
            "response_user_name" : "小火龙",
            "response_user_img" : "图片地址",
            "response_content" : [ 
                 "这是5号用户给刘德宝的回复", 
            ],
            "create_time" : "2017-15-12 14:00",
            ]
        }
    ]
}

其中的评论和回复的id,昵称,和头像是方便查询显示使用,并且id可以用来做消息推送

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jVWQEmhf-1599922848730)(\img\mongo11.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mj8uDoGF-1599922848731)(\img\mongo10.png)]

评论全部不存在。

一级评论未存在,全空,最下方有一级评论回复框,可以进行评论产生comment

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RNRRrubS-1599922848735)(\img\mongo09.png)]

一级评论存在,没有回复,点击回复,出现回复框,完成回复,即二级评论,可以回复,产生comment.response

同理对二级评论的回复也是通过点击生成回复框,然后进行回复。然后判断是否是回复的一级评论来控制是否显示回复两个字即可。

所以和二级评论实现是同一个逻辑,前台即可完成。

2.spring整合mongodb

整合部分最难的就是配置和熟悉对mongodb数据库的操作,其他的前台传参和逻辑编写大家都很熟悉。这里主要介绍一下mongodb操作的坑

mongoRepository和mongoTemplate

①、Spring Data MongoDB 是 Spring 框架访问 MongoDB 数据库的分支,使用它可以非常方便地操作 MongoDB 数据库。

Spring Data MongoDB 是 Spring Source 的一个子项目,旨在为关系型数据库、非关系型数据、Map-Reduce框架、云数据服务等等提供统一的数据访问API。

Spring Data 提供了基于Repository的统一接口 MongoRepository 完成对象的 CRUD 操作以及查询方法、排序和分页方法等。

使用 Spring Data 可以帮助我们快速构建项目,非常方便,Spring Data 在数据持久层已经写好了常用功能,我们只需要定义一个接口去继承 Spring Data 提供的接口,就可以实现对数据库的操作,也可以自定义接口方法,甚至这些自定义方法都不需要我们手动去实现,Reposity 会自动完成实现。

实现方法:

1.创建自定义接口 xxxRepository ,继承 MongoRepository,这样xxxRepository 就直接拥有了MongoRepository 中定义好的基本CRUD操作,同时可以完成方法扩展,只需要在xxxRepository 中按照规则声明抽象方法即可,MongoRepository 会自动完成方法实现,同时在类定义处添加@Repository 注解完成IoC注入。

2.创建自定义接口 xxxService 以及实现类xxxServiceImpl,并通过自动装载将xxxRepository 注入xxxServiceImpl。

3.创建 xxxController 实现相关业务方法。

spring-data-mongodb其实有两种实现方式,一种是直接继承MongoRepository接口,dao层的实现,默认提供了CRUD的各种方法,几乎不用编写任何代码。另一种是通过MongoTemplate来操作数据库,这样需要自定义dao层接口,默认没有任何接口可以使用

Criteria类:它封装所有的语句,以方法的形式进行查询。

Query类:这是将语句进行封装或者添加排序之类的操作。(实现分页操作就在这里)

mongoTemplate操作简介

1.添加依赖

<dependency>  
<groupId>org.springframework.data</groupId> 
<artifactId>spring-data-mongodb</artifactId>  
<version>2.1.1.RELEASE</version>
</dependency>

添加spring和mongodb的依赖

网上都说要加一个mongo的依赖类似于这种

org.mongodb mongo-java-driver 2.13.0

2.mongodb配置文件

mongodb.properties

#数据库名称
mongo.dbname=sag
#用户名
mongo.username=
#密码
mongo.password=
#主机
mongo.host=127.0.0.1
#端口
mongo.port=27017
#线程最大阻塞数
mongo.connectionsPerHost=8
#线程队列数mongo.threadsAllowedToBlockForConnectionMultiplier=4
#连接超时时间,单位毫秒
mongo.connectTimeout=1500
#最大等待时间
mongo.maxWaitTime=1500
#自动连接
mongo.autoConnectRetry=true
#socket存活
mongo.socketKeepAlive=true
#socket超时时间
mongo.socketTimeout=1500
#读写分离
mongo.slaveOk=true

spring-mongodb.xml

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xmlns:context="http://www.springframework.org/schema/context"       xmlns:mongo="http://www.springframework.org/schema/data/mongo"       xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd">   


<!--引入MongoDB连接文件-->  
<context:property-placeholder location="classpath:mongodb.properties" ignore-unresolvable="true"/>   
<!--连接MongoDB服务器-->   
<!-- 除此之外还有一个mongo连接,标签为<mongo-mongo> 版本较低时使用 -->   
<mongo:mongo-client id="mongo" host="${mongo.host}" port="${mongo.port}" >
<mongo:client-options               
connections-per-host="${mongo.connectionsPerHost}"
threads-allowed-to-block-for-connection-multiplier=
"${mongo.threadsAllowedToBlockForConnectionMultiplier}"
connect-timeout="${mongo.connectTimeout}"
max-wait-time="${mongo.maxWaitTime}"
socket-keep-alive="${mongo.socketKeepAlive}"
socket-timeout="${mongo.socketTimeout}"/> 
</mongo:mongo-client>  

<bean id="mappingContext"          			     class="org.springframework.data.mongodb.core.mapping.MongoMappingContext" /> 

<!-- 去掉默认的_class属性 -->  
<bean id="customMongoTypeMapper"          class="org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper">        <constructor-arg name="typeKey"><null/></constructor-arg>    
</bean>   

<bean id="mappingMongoConverter" class="org.springframework.data.mongodb.core.convert.MappingMongoConverter">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />       
<constructor-arg name="mappingContext" ref="mappingContext" />    
<property name="typeMapper" ref="customMongoTypeMapper" />
</bean>   

 <!-- mongo的工厂,通过它来取得mongo实例,dbname为mongodb的数据库名,没有的话会自动创建 -->
<mongo:db-factory id="mongoDbFactory"  dbname="${mongo.dbname}" mongo-ref="mongo"/> 

<!-- 配置mongoTemplate -->
<!-- mongodb的主要操作对象,所有对mongodb的增删改查的操作都是通过它完成-->   
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">     <constructor-arg name="mongoDbFactory" ref="mongoDbFactory"/>
<constructor-arg name="mongoConverter" ref="customMongoTypeMapper"></constructor-arg>
</bean>
</beans>

在Spring框架中,对mongodb进行操作的是mongoTemplate对象。

MongoTemplate是数据库和代码之间的接口,对数据库的操作都在它里面。

注:MongoTemplate是线程安全的。

在实现评论demo之前,我们先插入两个例子,感受一下mongotemplate

这里我建了一个POJO, comment

@Document(collection = "commenttest")
public class Comment {  
	//@id
    private String comment_id;         //评论id
    private String comment_blog_id;    //文章id
    private String comment_user_id;    //评论用户id
    private String comment_user_name;   //评论用户名
    private String comment_user_img;   //评论用户头像
    private String comment_content;    //评论内容
    private String comment_create_time;   //评论时间
    private String comment_responses;   //评论回复
}

然后相应的创建了一个CommentTestRepository类

@Repository
@Document(collection = "commenttest")
public class CommentTestRepository {    
    @Autowired   
    MongoTemplate mongoTemplate;  
    public CommentTestRepository() {    
        ApplicationContext ac = new ClassPathXmlApplicationContext("spring/spring-mongodb.xml");        
        mongoTemplate = (MongoTemplate) ac.getBean("mongoTemplate");    
    }    
    public void insertComment(Comment comment) {       
        List<Comment> list = new ArrayList<Comment>();    
        list.add(comment);      
        mongoTemplate.insert(list,Comment.class);   
}

http 通过 controller 进来的 mongotemplate 对象才会注入 spring ,才能正常使用,如果是通过其他 controller 类来调用同层的 controller 里面的 mongotemplate ,则该对象是 null 值,不可使用 !而从 controller 调用 service 这种不同层的,则可以正常使用!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mJoQEQ8O-1599922848736)(\img\mongo04.png)]

插入成功后,可以看到两个字段与我们预期的不太一样,_ id和_ class

_id:是系统自动生成的12字节唯一标识。满足分布式

mongodb采用了一个称之为ObjectId的类型来做主键。ObjectId是一个12字节的 BSON 类型字符串。按照字节顺序,一次代表:

4字节:UNIX时间戳
3字节:表示运行MongoDB的机器
2字节:表示生成此_id的进程
3字节:由一个随机数开始的计数器生成的值

https://blog.csdn.net/xiamizy/article/details/41521025

不使用ObjectId方法:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NoDJwLHu-1599922848738)(\img\mongo05.png)]

只需要把POJO实体类里的comment_id改名为id即可完成一种类似于覆盖的效果,达到了不使用objectId的目的。

或者在想要标识为id的属性上加上注解@id

_class:这个字段就是用来映射Pojo的,更具体的说,是为了方便处理Pojo中存在继承的情况,增加系统的扩展性的。去掉的方法就是配置Converter,默认映射为null

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nAT38MNK-1599922848739)(\img\mongo08.png)]

假如到时候设计不好,字段不确定怎么办。

个人建议是刚开始少一点,后面可以再加。

映射的时候,pojo里有的属性,数据库里没有,依然可以映射

数据库里有的字段,而pojo里没有对应的属性接受的话就无法完成映射了,

​ 比如我用户头像刚开始没加,只需要在对应的pojo类里,插入和查询逻辑里添加用户头像的属性和逻辑,就可以完成拓展,并不用像传统数据库再创建一个列,或者费心思再去创建几个冗余列。这就体现了mongodb的高拓展性。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mC3dN0ro-1599922848740)(\img\mongo06.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0Wj2EgQ2-1599922848740)(\img\mongo07.png)]

评论要点:

内嵌对象,比如comment对象里有一个response对象。那么mongotemplate映射到mongodb里的时候,对这个是怎么操作的呢。

根据实际情况,一级评论完毕,回复是在一级评论之后才有的操作,这个之前,一级评论文档中是没有任何回复的。所以这里检查了一下comment里如果没有这个键,或者这个键为空,可不可以插入到mongodb中。答案是可以的。

在测试的时候,尝试了两种方案。

1.在设置comment实例的时候,把对象设置为null

comment.setComment_responses(null);

2.不设置该属性,直接插入

结论:两者都不会插入该属性对应的数据。也不会存在这个属性对应的键值。不会自动映射进去

到时候要往comment里插入回复的时候,直接使用mongotemplate.upsert语句就可以完成。

如果查到一级评论没有任何回复,它就会创建response这个键,然后插入第一条记录

如果一级评论已经有回复,它就会插入到这个键里,成为这个键下的一个文档。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZU38yJlO-1599922848742)(\img\mongo12.png)]

mongoTemplate实现评论

通过手写插入一些假数据之后,我们开始查询,插入要和实际业务逻辑相对应,这里demo就不做了,大体思路是一级评论直接插入,二级评论,前台传入一级评论的id然后根据id插入,同时记录用户名和用户头像。

如果有,则前台显示有回复样式,没有的话,就是默认回复一级评论的二级评论。

查询实现

List<Comment> comments = mongoTemplate.find(query,Comment.class);
List<JSONObject> comments = mongoTemplate.find(query,JSONObject.class,"testMax2");       

query:查询条件,

.class:需要查询的类型

最后的字符串是需要查询的Collection,

采用后面这个语句会遍历的把整个Collection查询出来,包括其下面的responses

也可以不写,默认的是.class上注解的,但是这里采用了JSONObject.class这个类进行查询,我并没有对这个类进行注解,所以后面这个集合要写。

整合springboot

配置

在spring官网添加mongodb即可。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x6xK6CGO-1599922848744)(C:\Users\hasee\Desktop\mongodb实现评论\img\mongo13.png)]

先入为主的导入了Mybatis但没有引入Mysql,所以这里要引入

<dependency>  
<groupId>mysql</groupId> 
<artifactId>mysql-connector-java</artifactId>
</dependency>

其余的配置文件在打包上传的包里。

总结:

1.为什么评论要使用mongodb而不是mysql?

​ mongodb舍弃了事务管理,但是mongodb的功能是海量数据的查询很快,读写效率高,一般用于简单的sql查询或日志类数据的写入操作。

​ 在评论的业务中,最多的就是查询评论和发表评论了。

​ BSON文档结构的存储形式,查询很方便,不需要关联查询。

​ 其次,评论这种数据价值并不是特别大,但是量却很大,所以可以交给mongodb来存储

​ 在这种不需要事务管理,也不需要关联查询的情况下,使用mongodb可以得到更快的响应速度,更快捷的开发,更少的数据库设计。

2.为什么使用了内嵌模型而不是引用模型?

一对很少 one-to-few 可以采用内嵌文档

优点:不需要单独执行一条语句去获取内嵌的内容

缺点:无法把这些内嵌文档当做单独的实体去访问

适用场合:一对很少且不需要单独访问内嵌内容

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值