绿苑 發表於 2020-9-24 15:34:00

SpringBoot整合MongoDB(实现一个简单缓存)

<h2 id="前言">前言</h2>
<p>SpringBoot是常用开发框架,而MongoDB也是最近越来越火的非关系型数据库,这里使用SpringBoot+MongoDB实现一个小案例,当然MongoDB实际做缓存的可能不多,但是这里仅仅为了一个小demo简单的学习使用,入门上手为目的,更多的复杂查询还需关注MongoDB官网。</p>
<blockquote>
<p>如果本篇对你有帮助,还请点赞支持一下!原创作者:<code>bigsai</code></p>
</blockquote>
<p>如果对MongoDB不太了解,还请先看上篇MongoDB从立地到成佛。</p>
<h2 id="创建mongodb数据库和项目">创建MongoDB数据库和项目</h2>
<h3 id="创建mongodb数据库">创建MongoDB数据库</h3>
<p>打开Studio 3T数据库管理工具,连接本地MongoDB数据库之后,创建名为test的数据库,在test数据库中创建名为news得集合:</p>
<p><img src="https://img-blog.csdnimg.cn/20200822002245635.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNjkzMTcx,size_1,color_FFFFFF,t_70#pic_center"></p>
<h3 id="创建项目">创建项目</h3>
<p>首先,打开IDEA创建项目,选择创建Springboot项目:<br>
<img src="https://img-blog.csdnimg.cn/20200821210716683.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNjkzMTcx,size_1,color_FFFFFF,t_70#pic_center"><br>
然后在选择Gruop和Aritifact时候分别填写<strong>com</strong>和<strong>mongodemo</strong>,Java Version选择8版本。<br>
<img src="https://img-blog.csdnimg.cn/20200821212212572.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNjkzMTcx,size_1,color_FFFFFF,t_70#pic_center"></p>
<p>在勾选模块时候,这里勾选Spring web、MongoDB依赖模块,选择合适位置创建项目,项目就可以成功创建:</p>
<p><img src="https://img-blog.csdnimg.cn/20200821214801948.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNjkzMTcx,size_1,color_FFFFFF,t_70#pic_center"></p>
<h3 id="预备工作">预备工作</h3>
<p>创建完项目,我们需要做一些预备工作用来完成缓存。我们首先要在项目中的application.properties中添加配置连接到数据库,配置规则为:<code>spring.data.mongodb.uri=mongodb://地址:端口/数据库名</code>,本案例使用本地的MongoDB数据库,默认端口为27017,而使用的MongoDB具体数据库名称为test,那么就可以按照以下进行配置:</p>
<pre><code class="language-js">spring.data.mongodb.uri=mongodb://localhost:27017/test
</code></pre>
<p>这样在项目中就可以连接到本地的MongoDB的test数据库并访问。</p>
<p>其次在项目中com.mongodb目录下分别创建controller,service,pojo文件夹,在controller文件夹下创建<code>newsController.java</code>类,为负责url和逻辑的控制器:</p>
<pre><code class="language-java">package com.mongodemo.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class newsController {
    private static Logger logger= LoggerFactory.getLogger(newsController.class);
   
   
}

</code></pre>
<p>其中:</p>
<ul>
<li>@RestController就声明该类为一个控制器,并且返回JSON字符串给前端。</li>
<li>而Logger对象用于打印日志。在web项目中我们更倾向于使用log打印日志而不在控制台直接输出。</li>
</ul>
<p>controller创建完毕在service 文件夹下创建<code>NewsService.java</code>类,里面先编写以下内容:</p>
<pre><code class="language-java">package com.mongodemo.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;

@Service
public class NewsService {
    private static Logger logger= LoggerFactory.getLogger(NewsService.class);
    @Autowired
    MongoTemplate mongoTemplate;
   
}
</code></pre>
<p>其中:</p>
<ul>
<li>@Service 表示该类为一个service(事务处理),可以被注入到其他对象(Spring帮你管理)。</li>
<li>@Autowired表示要注入对象的意思。而MongoTemplate 就是已经封装好在Spring中操作MongoDB的对象。</li>
</ul>
<p>service创建完成,我们需要在pojo中创建news类,代表新闻实体内容。</p>
<pre><code class="language-java">import java.util.Date;

public class news {
    private String title;
    private Date date;
    private String brief;
    private String content;
    private String author;
    @Override
    public String toString() {
      return "news{" +
                "title='" + title + '\'' +
                ", date=" + date +
                ", brief='" + brief + '\'' +
                ", content='" + content + '\'' +
                ", author='" + author + '\'' +
                '}';
    }
    public news(String title, Date date, String brief, String content, String author) {
      this.title = title;
      this.date = date;
      this.brief = brief;
      this.content = content;
      this.author = author;
    }
    public String getTitle() {
      return title;
    }
    public void setTitle(String title) {
      this.title = title;
    }
    public Date getDate() {
      return date;
    }
    public void setDate(Date date) {
      this.date = date;
    }
    public String getBrief() {
      return brief;
    }
    public void setBrief(String brief) {
      this.brief = brief;
    }
    public String getContent() {
      return content;
    }
    public void setContent(String content) {
      this.content = content;
    }
    public String getAuthor() {
      return author;
    }
    public void setAuthor(String author) {
      this.author = author;
    }
}

</code></pre>
<p>其中各个字段分别表示为:</p>
<table>
<thead>
<tr>
<th>名称</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td>title</td>
<td>标题</td>
</tr>
<tr>
<td>date</td>
<td>日期</td>
</tr>
<tr>
<td>brief</td>
<td>概要</td>
</tr>
<tr>
<td>content</td>
<td>内容</td>
</tr>
<tr>
<td>author</td>
<td>作者</td>
</tr>
</tbody>
</table>
<h2 id="缓存查询">缓存查询</h2>
<p>下面开始实战MongoDB实现一个新闻得缓存功能,实现缓存之前,要清楚缓存的核心作用:提升web程序的<strong>查询速度</strong>,将热点数据放到非关系数据库中。本案例对接口进行缓存,不过真正的缓存实例需要考虑很多问题比如时效性,缓存那些数据等。在这里主要为了讲解MongoDB的一个实例。</p>
<p>在查询时候,缓存和数据库之间通常是这么配合的:</p>
<p><img src="https://img-blog.csdnimg.cn/20200822082605474.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNjkzMTcx,size_1,color_FFFFFF,t_70#pic_center"></p>
<p>为了降低整个项目的复杂度,这里用手动生成的数据对象代替成数据库中查询的数据,我们在NewsService中编写getNewsByTitle(String title)函数,其功能是根据标题返回缓存或数据库中该条news数据,如果MongoDB中存在则直接返回该对象,否则先从数据库查询(这里直接生成),然后存到MongoDB中再返回。具体代码为:</p>
<pre><code class="language-java">public news getNewsByTitle(String title)
   {
       //查询数据先从MongoDB中查询
       Query query = new Query(Criteria.where("title").is(title));
       news news=mongoTemplate.findOne(query, news.class);
       if(news==null)//缓存中没该条记录
       {
         logger.info("从数据库查询数据");
         //假设news1从数据库中查询
         news news1=new news(title,new Date(),"","","bigsai");
         news1.setBrief("有了博学谷,妈妈再也不用担心我的java学习!");
         news1.setContent("博学谷优质学习资料为java学习提供更好环境,越来越多开发者学习使用");
         mongoTemplate.insert(news1,"news");
         logger.info("数据插入到MongoDB成功");
         news=news1;
       }
       else {
         logger.info("数据从缓存访问成功");
       }
       returnnews;
   }
</code></pre>
<p>上面的代码中:</p>
<ul>
<li>我们核心使用mongoTemplate对象来实现查询一条记录,查询语句为:mongoTemplate.findOne(query, news.class),第一个参数为查询的条件,第二个参数为查询结果转成Java对象的类型,它帮你自动处理。</li>
<li>通过Query对象来辅助我们实现条件查询,这里的意思就是查询条件为:MongoDB中title字段为传进来title字符串的该条记录。</li>
<li>而插入的语法为 mongoTemplate.insert(news1,"news"),第一个参数为插入的文档记录,第二个参数为连接呃MongoDB对应数据库下的集合(Collections)。</li>
</ul>
<p>在newsController中,我们编写一个名称为getnews的接口,用来给用户返回该标题新闻(news类)的一条数据的JSON文件,具体代码为:</p>
<pre><code class="language-java">@Autowired
   NewsService newsService;

   @GetMapping("getnews/{title}")
   public news getnews(@PathVariable String title)
   {
      news news=newsService.getNewsByTitle(title);
       returnnews;
   }
</code></pre>
<p>上面代码中:</p>
<ul>
<li>@Autowired(required = false)用来注入对象,下面的NewsService userService就是被注入的对象,注入之后不需要手动创建对象可以直接使用(Spring帮你管理)</li>
<li>@GetMapping("getnews/{title}") 意为声明一个get请求方式的接口,</li>
</ul>
<p>我们启动程序,浏览器输入<code>localhost:8080/getnews/好好学java</code> 页面会有返回的结果,返回的一个news对象序列化成JSON的字符串的文本。<br>
<img src="https://img-blog.csdnimg.cn/20200822092224899.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNjkzMTcx,size_1,color_FFFFFF,t_70#pic_center"></p>
<p>同时,你查看IDEA的日志,由于第一次查询,MongoDB中没有对应数据你会发现会先从数据库中查询然后存储到MongoDB中:<br>
<img src="https://img-blog.csdnimg.cn/20200822093748293.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNjkzMTcx,size_1,color_FFFFFF,t_70#pic_center"><br>
查看MongoDB的news集合发现记录被成功插入了,多刷新页面<code>localhost:8080/getnews/好好学java</code>你会发现数据会直接从MongoDB中返回:<br>
<img src="https://img-blog.csdnimg.cn/20200822094157561.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNjkzMTcx,size_1,color_FFFFFF,t_70#pic_center"></p>
<h2 id="缓存更新删除">缓存更新、删除</h2>
<p>缓存中的数据和存储的关系数据库的数据是一致的,当我们只有查询操作的时候,可以一直保持数据的一致性,但是我们如果对数据有更新、删除的操作,就需要对关系数据库和MongoDB中的数据同时进行更新或删除的操作,让数据再次达到一致性的效果。</p>
<h3 id="缓存更新">缓存更新</h3>
<p>虽然大部分情况我们对热点新闻数据可能很少更新,但是也有时候新闻中有部分内容需要更改的需要我们完成,比如比分错字或者不妥的言论。</p>
<p>我们在NewsService中编写updateNewsContentByTitle((String title,String content)函数,其作用为更新数据库(这里没有具体实现)和MongoDB缓存中的数据:</p>
<pre><code class="language-java"> public boolean updateNewsContentByTitle(String title,String content)
    {
      try {
            Query query = new Query(Criteria.where("title").is(title));
            Update update = new Update();
            update.set("content", content);//更新内容
            update.set("date",new Date());//更新时间
            // 假设在这里数据库中更新过这里跳过
            
            // updateFirst 更新查询返回结果集的第一条
            //upsert 更新如果不存在就插入
            mongoTemplate.upsert(query, update, news.class);
      }
      catch (Exception e)
      {
            returnfalse;
      }
      returntrue;
    }
</code></pre>
<p>其中:</p>
<ul>
<li>Query对象来辅助我们实现条件查询待更新数据,这里的意思就是查询条件同样为:MongoDB中title字段为传进来title字符串的该条记录。</li>
<li>Update对象用来记录更新的字段和数据,这里更新传进来的content内容和date日期。</li>
<li>mongoTemplate.upsert(query, update, news.class)用来实现更新,如果MongoDB中不存在该数据那么就插入到MongoDB中。</li>
</ul>
<p>编写完service,在newsController中编写一个名为updatenews的接口,用来更新数据库数据和缓存在MongoDB的数据:</p>
<pre><code class="language-java"> @GetMapping("updatenews")
public String updatenews(String title,String content)
{
   boolean bool=newsService.updateNewsContentByTitle(title,content);
   if(bool)
         return"更新成功";
   else
         return"更新失败";
}
</code></pre>
<p>启动程序访问<code>localhost:8080/updatenews?title=好好学java&amp;content=学好java走遍全天下</code>,你会发现数据更新成功:<br>
<img src="https://img-blog.csdnimg.cn/20200822230840403.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNjkzMTcx,size_1,color_FFFFFF,t_70#pic_center"></p>
<h3 id="缓存删除">缓存删除</h3>
<p>除了更新的时候需要保证数据一致性,删除的时候也需要保证数据一致性,如果在删除关系数据库的数据而不删除MongoDB缓存,那么下次查询该条数据MongoDB中存在而关系数据库中不存在,这样就造成了数据不一致,所以在删除数据的时候我们需要在MongoDB中的数据也删除。</p>
<p>在NewsService中编写deleteNewsByTitle(String title)函数,用来根据标题title删除MongoDB中的记录:</p>
<pre><code class="language-java"> publicboolean deleteNewsByTitle(String title)
    {
      try {
            Query query = new Query(Criteria.where("title").is(title));
            mongoTemplate.remove(query,news.class);
      }
      catch (Exception e)
      {
            returnfalse;
      }
      returntrue;
    }
</code></pre>
<p>mongoTemplate.remove(query,news.class);意味从MongoDB中删除满足查询条件的记录。其中query为查询条件,news.class为删除对象在Java中的类。</p>
<p>在newsController中编写deletenews接口,用来处理删除的请求:</p>
<pre><code class="language-java"> @GetMapping("deletenews/{title}")
public String deletenews(@PathVariable String title)
{
   try {
         newsService.deleteNewsByTitle("好好学java");
         return "删除成功";
   }
   catch (Exception e)
   {
         return "删除失败";
   }
}
</code></pre>
<p>启动程序,访问<code>http://localhost:8080/deletenews/好好学java</code>,会发现缓存在MongoDB中的记录被成功删除,这样就保证MongoDB中不会出现关系数据库中不存在的脏数据,达到数据一致性!<br>
<img src="https://img-blog.csdnimg.cn/20200823003706916.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNjkzMTcx,size_1,color_FFFFFF,t_70#pic_center"></p>
<p>本篇到这里就结束了,如果帮助还请不要吝啬你的<strong>小赞、收藏</strong>一份如有更多期待还请关注公众号<code>bigsai</code>,回复bigsai获取珍藏pdf资源一份!</p><br><br>
来源:https://www.cnblogs.com/bigsai/p/13724341.html
頁: [1]
查看完整版本: SpringBoot整合MongoDB(实现一个简单缓存)