SpringBoot整合MongoDB(附MongoDB做缓存demo源码)

SpringBoot整合MongoDB

之前我们讲解了MongoDB在不同环境的安装MongoDB的CRUD以及Robo3T可视化工具操作MongoDB,今天我们来看看实际业务中整合MongoDB的场景。

本文我们主要是通过两种方法实现SpringBoot整合MongoDB,最后写一个小demo来理解MongoDB在实际业务中的应用。

整合

新建项目

新建一个SpringBoot项目,只需要勾选SpringWeb和SpringDataMongoDB的模块即可

image-20201012091458573

通过Robo3T连接MongoDB数据库,确保数据库可用,也可以实时监测数据库情况。如果还没有配置Robo3T的可以看我这一篇博客

image-20201012091805814

在MongoDB中新建一个databases,一会儿要用。

SpringBoot中配置类

因为MongoDB默认是无密码的,我们这边没密码就空着

#写服务器地址,本地就localhost
spring.data.mongodb.host=localhost
#用户名密码不同的库需要不同的认证,这里是在 admin 库中
spring.data.mongodb.authentication-database=admin
#有账户和密码就写,没有就空着
spring.data.mongodb.username=
spring.data.mongodb.password=
spring.data.mongodb.port=27017
#这里写刚才创建的数据库
spring.data.mongodb.database=feng

我们先用MongoRepository接口来实现整合的方法

在项目中创建一个实体类

package com.feng.pojo;

/**
 * <h3>spirngboot-mongodb-test</h3>
 * <p></p>
 *
 * @author : Nicer_feng
 * @date : 2020-10-12 09:34
 **/
public class User {
    private Integer id;
    private String name;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public User(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

创建一个UserDao接口,这个接口要继承MongoRepository接口

我们可以发现MongoRepository接口中已经封装好了一些基础的方法,查询,增加等

image-20201012094100941

UserDao.java

package com.feng.dao;

import com.feng.pojo.User;
import org.springframework.data.mongodb.repository.MongoRepository;

/**
 * <h3>spirngboot-mongodb-test</h3>
 * <p></p>
 *
 * @author : Nicer_feng
 * @date : 2020-10-12 09:35
 **/
public interface UserDao extends MongoRepository<User,Integer> {
	//啥也不用干,继承上面那个接口就行
}

然后我们去测试类测试增加数据

测试连接和插入

package com.feng;

import com.feng.dao.UserDao;
import com.feng.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class SpirngbootMongodbTestApplicationTests {

    @Autowired
    UserDao userDao;

    @Test
    void contextLoads() {
    }

    @Test
    public void addUser(){
        User user = new User();
        user.setId(1);
        user.setName("feng");
        userDao.insert(user);
    }

}

运行测试方法,可以发现测试成功

image-20201012094448157

在Robo3T中查询数据库,发现增加数据成功

image-20201012094532637

当然我们也可以不用他MongoRepository封装的方法,我们可以自定义方法

自定义接口方法

我们在MongoDB中多插入两个数据

image-20201012095114915

在UserDao.java中加入方法

public interface UserDao extends MongoRepository<User,Integer> {
    List<User> findUserByNameContaining(String name);
}

测试类实现

package com.feng;

import com.feng.dao.UserDao;
import com.feng.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
class SpirngbootMongodbTestApplicationTests {

    @Autowired
    UserDao userDao;

    //******

    @Test
    void findUserByNameContaining(){
        List<User> all = userDao.findAll();
        System.out.println(all);
        List<User> feng = userDao.findUserByNameContaining("fe");
        System.out.println(feng);
    }

}

查询结果

image-20201012095219594

除了继承MongoRepository接口,我们还可以直接调用MongoTemplate,和RedisTemplate差不多,就是封装好的模板,直接创建对象调用对应方法即可。

MongoTemplate实现

直接去测试类

package com.feng;

import com.feng.dao.UserDao;
import com.feng.pojo.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.mongodb.core.MongoTemplate;

import java.util.List;

@SpringBootTest
class SpirngbootMongodbTestApplicationTests {

    //****************


    @Autowired
    MongoTemplate mongoTemplate;
    @Test
    void testTemplate(){
        User user = new User();
        user.setId(4);
        user.setName("冯半仙");
        mongoTemplate.insert(user);
        List<User> all = mongoTemplate.findAll(User.class);
        System.out.println(all);
    }
}

可以看到findAll方法的参数必须是实体类的Class,所以我们传入User.class

image-20201012095700418

测试运行

可以看到数据插入成功,并且查询成功

image-20201012095921262

实际业务情况

我们知道非关系型数据库MongoDB一般就是做缓存功能,来提高web的程序的查询速度,将热点数据放在非关系型数据库中,而不是每次都去关系型数据库中查询的(如mysql),以下我们讲一个小demo,来模拟MongoDB做缓存的情况。但我们要知道实际业务中远比这复杂,比如缓存的时效性,哪些是热点数据(哪些数据需要缓存)等,这里的小demo并不考虑这些,只是参考一下思想

我们在进行查询时,缓存和数据库一般是这样配合的

image-20201012100926184

MongoDB做缓存小Demo

数据库

懒得新建项目也可以拿刚才的直接测哈,在数据里找了一圈发现有一个叫blog的比较合适,先看看数据表长什么样

image-20201012111033664

建表语句

CREATE TABLE `blog` (
  `id` varchar(50) NOT NULL COMMENT '博客id',
  `title` varchar(100) NOT NULL COMMENT '博客标题',
  `author` varchar(30) NOT NULL COMMENT '博客作者',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `views` int(30) NOT NULL COMMENT '浏览量'
) ENGINE=InnoDB DEFAULT CHARSET=utf8

在SpringBoot项目中加入mysql依赖

<!--JDBC-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--MYSQL-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

在配置文件application.properties中加入mysql配置

spring.datasource.username=root
#自己的mysql数据库密码
spring.datasource.password=*****
#自己把上面的表建立在哪个库中,就连哪个库
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

创建实体类

Blog.java

package com.feng.pojo;

import java.util.Date;

/**
 * <h3>spirngboot-mongodb-test</h3>
 * <p></p>
 *
 * @author : Nicer_feng
 * @date : 2020-10-12 10:10
 **/
public class Blog {
    private Integer id;
    private String title;
    private String author;
    private Date create_time;
    private Integer views;

    public Blog() {
    }

    public Blog(Integer id, String title, String author, Date create_time, Integer views) {
        this.id = id;
        this.title = title;
        this.author = author;
        this.create_time = create_time;
        this.views = views;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public Date getCreate_time() {
        return create_time;
    }

    public void setCreate_time(Date create_time) {
        this.create_time = create_time;
    }

    public Integer getViews() {
        return views;
    }

    public void setViews(Integer views) {
        this.views = views;
    }

    @Override
    public String toString() {
        return "Blog{" +
                "id=" + id +
                ", title='" + title + '\'' +
                ", author='" + author + '\'' +
                ", create_time=" + create_time +
                ", views=" + views +
                '}';
    }
}

测试

直接去SpringBoot测试类,我们什么都先用模板类实现,这里面我们先测试一个查询,代码逻辑就是先从缓存(MongoDB)中查,如果缓存中有,直接返回,如果缓存中没有,我们去关系型数据库(MySQL)中查询,并将查询的结果存入缓存中,下次再查询直接返回结果。

package com.feng;

import com.feng.dao.UserDao;
import com.feng.pojo.Blog;
import com.feng.pojo.User;
import org.junit.jupiter.api.Test;
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.jdbc.core.JdbcTemplate;

import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

@SpringBootTest
class SpirngbootMongodbTestApplicationTests {

    //********
    
    @Autowired
    MongoTemplate mongoTemplate;
    
    @Autowired
    JdbcTemplate jdbcTemplate;

    @Test
    void getBlogByTitle() throws SQLException {
        String title = "Java如此简单2";
        Query query = new Query(Criteria.where("title").is(title));
        Blog one = mongoTemplate.findOne(query, Blog.class);
        if(one!=null){
            System.out.println("从缓存数据库直接拿的数据:"+one);
        }else{
            String sql = "SELECT * FROM mybatis.blog WHERE blog.title = ?";
            List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql,"Java如此简单2");

            System.out.println("从mysql数据库拿的数据:"+maps.toString());
            Map<String, Object> map = maps.get(0);
            Set<String> strings = map.keySet();
            System.out.println(strings);

            Blog blog = new Blog();
            blog.setId(Integer.valueOf(map.get("id").toString()));
            blog.setTitle((String) map.get("title"));
            blog.setAuthor((String) map.get("author"));
            blog.setCreate_time((Date) map.get("create_time"));
            blog.setViews((Integer) map.get("views"));
            System.out.println(blog.toString());
            
            mongoTemplate.insert(blog);
            //这里不能直接把从jdbc模板类返回的对象存到MongoDB中,因为queryForList返回的对象并不是我们的实体类对象,我们要手动转存再存到缓存中
            //mongoTemplate.insert(maps);
            System.out.println("mysql拿的数据存到缓存中啦!");
        }
    }

}

第一次查询,缓存中无结果,在数据库中查到,并存入缓存中

整合MongoDB1

第二次查询,直接从缓存中拿到结果并返回

整合MongoDB2

那既然思路和测试没问题,咱模拟的像一点,整个业务出来吧

在项目中创建controller和service文件夹

image-20201012135154394

分别创建BlogController和BlogService

BlogController.java

package com.feng.controller;

import com.feng.pojo.Blog;
import com.feng.service.BlogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

/**
 * <h3>spirngboot-mongodb-test</h3>
 * <p></p>
 *
 * @author : Nicer_feng
 * @date : 2020-10-12 11:27
 **/

@RestController
public class BlogController {

    @Autowired
    BlogService blogService;

    @GetMapping("getBlog/{title}")
    public Blog getBlog(@PathVariable String title){
        Blog blogByTitle = blogService.getBlogByTitle(title);
        return blogByTitle;
    }
}
  • 我们这里类名使用@RestController注解声明该类为一个控制器,并且返回JSON字符串给前端。

BlogService.java

package com.feng.service;

import com.feng.pojo.Blog;
import org.springframework.beans.factory.annotation.Autowired;
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.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * <h3>spirngboot-mongodb-test</h3>
 * <p></p>
 *
 * @author : Nicer_feng
 * @date : 2020-10-12 11:27
 **/
@Service
public class BlogService {
    @Autowired
    MongoTemplate mongoTemplate;
    @Autowired
    JdbcTemplate jdbcTemplate;

    public Blog getBlogByTitle(String title){
        Query query = new Query(Criteria.where("title").is(title));
        Blog one = mongoTemplate.findOne(query, Blog.class);
        if(one!=null){
            System.out.println("从缓存数据库直接拿的数据:"+one);
        }else{
            String sql = "SELECT * FROM mybatis.blog WHERE blog.title = ?";
            List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql,title);

            System.out.println("从mysql数据库拿的数据:"+maps.toString());
            Map<String, Object> map = maps.get(0);
            Set<String> strings = map.keySet();
            System.out.println(strings);

            Blog blog = new Blog();
            blog.setId(Integer.valueOf(map.get("id").toString()));
            blog.setTitle((String) map.get("title"));
            blog.setAuthor((String) map.get("author"));
            blog.setCreate_time((Date) map.get("create_time"));
            blog.setViews((Integer) map.get("views"));
            mongoTemplate.insert(blog);
            one=blog;
            System.out.println("mysql拿的数据存到缓存中啦!");
        }

        return one;
    }
}

这里面的逻辑跟这张图一致

image-20201012100926184

  • @Service 表示该类为一个service(事务处理),可以被注入到其他对象(Spring帮你管理)。
  • @Autowired表示要注入对象的意思。而MongoTemplate 就是已经封装好在Spring中操作MongoDB的对象。

测试

启动SpringBoot启动类,访问8080端口

第一次我们查询缓存中有的数据,第二次查询缓存中不存在的数据,可以看到第一次直接返回缓存中的结果,而第二次是先从数据库中拿,然后将数据保存到缓存中,再返回给前端

整合MongoDB3

缓存需要注意的问题

我们是用缓存保存热点数据的时候可能很少有更新或者删除的请款,但并不代表我们遇不到这种情况,所以我们在BlogService中还要添加一个updateBlogByTitle(String title,Integer views),方法主要是用来更新数据库和MongoDB缓存中的数据库

缓存数据的修改

注意缓存一致性

在BlogSerivce中添加update方法

public boolean updateBlogByTitle(String title,Integer views){
        try {
            Query query = new Query(Criteria.where("title").is(title));
            Update update = new Update();
            update.set("views",views);
            mongoTemplate.upsert(query,update,Blog.class);
            String sql  = "update mybatis.blog set views=? where title=?";
            int isUpdate = jdbcTemplate.update(sql, views, title);
            if(isUpdate==0){
                return false;
            }
        } catch (Exception e) {
            return false;
        }
        return true;
    }

controller配置对应视图层

@GetMapping("updateBlog")
    public String updateBlog(String title,Integer views){
        boolean b = blogService.updateBlogByTitle(title, views);
        return b?"update success":"update failed";
    }

运行

运行SpringBoot主类,访问项目地址

http://localhost:8080/updateBlog?title=Java如此简单&views=5533

可以发现缓存和数据库中的数据均更新成功

在这里插入图片描述

缓存数据的删除

service添加删除逻辑

public boolean deleteBlogByTitle(String title){
        try {
            Query query = new Query(Criteria.where("title").is(title));
            mongoTemplate.remove(query, Blog.class);
            String sql = "delete from mybatis.blog where title = ?";
            int isDel = jdbcTemplate.update(sql, title);
            if(isDel==0){
                return false;
            }
        } catch (Exception e) {
            return false;
        }
        return true;
    }

controller添加视图

@GetMapping("delete/{title}")
    public String deleteBlog(@PathVariable String title){
        boolean b = blogService.deleteBlogByTitle(title);
        return b?"delete success":"delete failed";
    }

测试

访问 http://localhost:8080/delete/MongoDB如此简单 发现缓存和数据库中的数据均被删除

整合MongoDB5

最后添加的逻辑也基本一致,这里不再赘述,感兴趣的同学可以继续测试!做缓存时最重要的就是数据的一致性,千万不要出现缓存和数据库中的数据不一致的情况。

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

上上签i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值