这里学习下webflux和mongodb的结合,使用mongodb来进行数据的存储,使用docker来启动mongodb容器简化开发。docker资料学习可以参考:https://www.jianshu.com/p/f272726db9c5

这篇博客的学习来源是http://gitbook.cn/gitchat/column/5acda6f6d7966c5ae1086f2b/topic/5acda9f9d7966c5ae108705c

        详细资料可以参考源文,这里只是做一个学习的总结和实践的过程。springboot和mongodb的使用可以参考我之前的博客:https://blog.csdn.net/j903829182/article/details/78197110

        环境:虚拟机cenos7,安装了docker,jdk1.8,IDEA

        mongodb参考资料:https://www.mongodb.com/

       什么是mongodb:

MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。

MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。他支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。

  一 docker 安装mongodb 

1,安装好docker

2,使用docker从阿里或者网易镜像库拉取mongodb的镜像,速度比较快


      上图我是从网易的docker镜像仓库里面拉取的一个mongodb镜像,网易蜂巢改版了,一下没找到仓库地址了,这里有一个阿里云的docker仓库地址:https://dev.aliyun.com/search.html

3,创建挂载目录

docker volume create mongo_data_db
docker volume create mongo_data_configdb


4,启动mongodb

docker run -d \
    --name mongo \
    -v mongo_data_configdb:/data/configdb \
    -v mongo_data_db:/data/db \
    -p 27017:27017 \
    ed27e79af407 \
    --auth jack 


其中ed27e79af407为mongodb的镜像id



上图是启动mongodb成功的图

5,初始化管理员账号

docker exec -it mongo     mongo              admin
                        // 容器名   // mongo命令 数据库名

# 创建最高权限用户
db.createUser({ user: 'admin', pwd: 'admin', roles: [ { role: "root", db: "admin" } ] });


说明:“docker exec -it mongo     mongo              admin“命令是进入到容器里面执行命令

如果此时需要退出容器里面的使用快捷键:ctr +p+q


MongoDB的基本命令:

类似 MySQL 命令,显示库列表:

show dbs

> use admin
switched to db admin
> show collections
使用某数据库:

use admin

> use admin
switched to db admin
显示表列表:

show collections

> show collections
system.users
system.version

如果存在 city 表,格式化显示 city 表内容:
db.city.find().pretty()

二 项目开发

1,创建一个spring boot项目

pom.xml代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.jack</groupId>
	<artifactId>webflux_mongodb</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>webflux_mongodb</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.2.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-webflux</artifactId>
		</dependency>

		<!-- Spring Boot 响应式 MongoDB 依赖 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
		</dependency>


		<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.16.20</version>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>io.projectreactor</groupId>
			<artifactId>reactor-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>


</project>

2,配置mongodb的连接配置

修改application.properties,如下:

spring.data.mongodb.host=192.168.0.104
spring.data.mongodb.database=admin
spring.data.mongodb.port=27017
spring.data.mongodb.username=admin
spring.data.mongodb.password=admin

3,创建一个city类

代码如下:

package com.jack.webflux_mongodb.domain;

import lombok.Data;
import org.springframework.data.annotation.Id;

/**
 * create by jack 2018/5/12
 * 城市实体类
 */
@Data
public class City {
    /**
     * 城市编号
     * @Id 注解标记对应库表的主键或者唯一标识符。因为这个是我们的 DO,数据访问对象一一映射到数据存储。
     */
    @Id
    private Long id;

    /**
     * 省份编号
     */
    private Long provinceId;

    /**
     * 城市名称
     */
    private String cityName;

    /**
     * 描述
     */
    private String description;
}

4,MongoDB 数据访问层 CityRepository

修改 CityRepository 类,代码如下:

package com.jack.webflux_mongodb.dao;

import com.jack.webflux_mongodb.domain.City;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import org.springframework.stereotype.Repository;

/**
 * create by jack 2018/5/12
 */
@Repository
public interface CityRepository extends ReactiveMongoRepository<City, Long> {
}

CityRepository 接口只要继承 ReactiveMongoRepository 类即可,默认会提供很多实现,比如 CRUD 和列表查询参数相关的实现。ReactiveMongoRepository 接口默认实现了如下:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.data.mongodb.repository;

import org.reactivestreams.Publisher;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.query.ReactiveQueryByExampleExecutor;
import org.springframework.data.repository.reactive.ReactiveSortingRepository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@NoRepositoryBean
public interface ReactiveMongoRepository<T, ID> extends ReactiveSortingRepository<T, ID>, ReactiveQueryByExampleExecutor<T> {
    <S extends T> Mono<S> insert(S var1);

    <S extends T> Flux<S> insert(Iterable<S> var1);

    <S extends T> Flux<S> insert(Publisher<S> var1);

    <S extends T> Flux<S> findAll(Example<S> var1);

    <S extends T> Flux<S> findAll(Example<S> var1, Sort var2);
}

ReactiveMongoRepository 的集成接口ReactiveSortingRepository、ReactiveQueryByExampleExecutor。

ReactiveSortingRepository接口代码如下:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.data.repository.reactive;

import org.springframework.data.domain.Sort;
import org.springframework.data.repository.NoRepositoryBean;
import reactor.core.publisher.Flux;

@NoRepositoryBean
public interface ReactiveSortingRepository<T, ID> extends ReactiveCrudRepository<T, ID> {
    Flux<T> findAll(Sort var1);
}

ReactiveCrudRepository接口有很多方法如下:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.data.repository.reactive;

import org.reactivestreams.Publisher;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.Repository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@NoRepositoryBean
public interface ReactiveCrudRepository<T, ID> extends Repository<T, ID> {
    <S extends T> Mono<S> save(S var1);

    <S extends T> Flux<S> saveAll(Iterable<S> var1);

    <S extends T> Flux<S> saveAll(Publisher<S> var1);

    Mono<T> findById(ID var1);

    Mono<T> findById(Publisher<ID> var1);

    Mono<Boolean> existsById(ID var1);

    Mono<Boolean> existsById(Publisher<ID> var1);

    Flux<T> findAll();

    Flux<T> findAllById(Iterable<ID> var1);

    Flux<T> findAllById(Publisher<ID> var1);

    Mono<Long> count();

    Mono<Void> deleteById(ID var1);

    Mono<Void> deleteById(Publisher<ID> var1);

    Mono<Void> delete(T var1);

    Mono<Void> deleteAll(Iterable<? extends T> var1);

    Mono<Void> deleteAll(Publisher<? extends T> var1);

    Mono<Void> deleteAll();
}

另外可以看出,接口的命名是遵循规范的,常用命名规则如下:

关键字 方法命名
And 》 findByNameAndPwd
Or 》findByNameOrSex
Is 》findById
Between 》 findByIdBetween
Like  》 findByNameLike
NotLike  》 findByNameNotLike
OrderBy  》 findByIdOrderByXDesc
Not     》 findByNameNot


常用代码示例:

Flux<Person> findByLastname(String lastname);

    @Query("{ 'firstname': ?0, 'lastname': ?1}")
    Mono<Person> findByFirstnameAndLastname(String firstname, String lastname);

    // Accept parameter inside a reactive type for deferred execution
    Flux<Person> findByLastname(Mono<String> lastname);

    Mono<Person> findByFirstnameAndLastname(Mono<String> firstname, String lastname);

    @Tailable // Use a tailable cursor
    Flux<Person> findWithTailableCursorBy();

方法的命名和JPA的命名有相似

5,处理器类 Handler 和控制器类 Controller

修改下 Handler,代码如下:

package com.jack.webflux_mongodb.handler;

import com.jack.webflux_mongodb.dao.CityRepository;
import com.jack.webflux_mongodb.domain.City;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * create by jack 2018/5/12
 */
@Component
public class CityHandler {
    private final CityRepository cityRepository;

    @Autowired
    public CityHandler(CityRepository cityRepository) {
        this.cityRepository = cityRepository;
    }

    public Mono<City> save(City city) {
        return cityRepository.save(city);
    }

    public Mono<City> findCityById(Long id) {

        return cityRepository.findById(id);
    }

    public Flux<City> findAllCity() {

        return cityRepository.findAll();
    }

    public Mono<City> modifyCity(City city) {

        return cityRepository.save(city);
    }

    public Mono<Long> deleteCity(Long id) {
        cityRepository.deleteById(id);
        return Mono.create(cityMonoSink -> cityMonoSink.success(id));
    }
}

6,控制器代码如下:

package com.jack.webflux_mongodb.webflux.controller;

import com.jack.webflux_mongodb.domain.City;
import com.jack.webflux_mongodb.handler.CityHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * create by jack 2018/5/12
 */
@RestController
@RequestMapping(value = "/city")
public class CityWebFluxController {
    @Autowired
    private CityHandler cityHandler;

    @GetMapping(value = "/{id}")
    public Mono<City> findCityById(@PathVariable("id") Long id) {
        return cityHandler.findCityById(id);
    }

    @GetMapping()
    public Flux<City> findAllCity() {
        return cityHandler.findAllCity();
    }

    @PostMapping()
    public Mono<City> saveCity(@RequestBody City city) {
        return cityHandler.save(city);
    }

    @PutMapping()
    public Mono<City> modifyCity(@RequestBody City city) {
        return cityHandler.modifyCity(city);
    }

    @DeleteMapping(value = "/{id}")
    public Mono<Long> deleteCity(@PathVariable("id") Long id) {
        return cityHandler.deleteCity(id);
    }
}

7,运行程序

使用postman进行测试:



测试结果如上图

8,连接 MongoDB,验证数据
连接 MongoDB:

docker run -it --rm --link mongo:mongo mongo mongo -u admin -p admin --authenticationDatabase admin mongo/admin
[root@bogon ~]# docker run -it --rm --link mongo:mongo mongo mongo -u admin -p admin --authenticationDatabase admin mongo/admin
Unable to find image 'mongo:latest' locally
Trying to pull repository docker.io/library/mongo ... 
latest: Pulling from docker.io/library/mongo

4d0d76e05f3c: Already exists 
2da2ecd7fdbd: Already exists 
c3a86da34d0f: Already exists 
e2b1f447e420: Already exists 
c9e820834b36: Already exists 
ffa34fa64bf4: Already exists 
63127ea58ee0: Already exists 
ccb46836c598: Already exists 
7b0abf374ec4: Already exists 
Digest: sha256:c6d2b2f8c054210db26b492bab81ffab171ee54eb58925fa98fabb4faca3a9cb
MongoDB shell version v3.6.4
connecting to: mongodb://mongo:27017/admin
MongoDB server version: 3.2.0
WARNING: shell and server versions do not match
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
	http://docs.mongodb.org/
Questions? Try the support group
	http://groups.google.com/group/mongodb-user
2018-05-12T03:32:38.834+0000 I STORAGE  [main] In File::open(), ::open for '/home/mongodb/.mongorc.js' failed with No such file or directory
Server has startup warnings: 
2018-05-12T10:36:02.745+0800 I CONTROL  [initandlisten] 
2018-05-12T10:36:02.745+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
2018-05-12T10:36:02.745+0800 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2018-05-12T10:36:02.745+0800 I CONTROL  [initandlisten] 
2018-05-12T10:36:02.745+0800 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.
2018-05-12T10:36:02.745+0800 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2018-05-12T10:36:02.745+0800 I CONTROL  [initandlisten] 
> 

显示数据库列表:

> show dbs
admin  0.000GB
local  0.000GB
> 

使用某数据库:

> use admin
switched to db admin
> 

显示所有的表:

> show collections
city
system.users
system.version
> 

如果存在city表,格式化输出city表的内容

> db.city.find().pretty()
{
	"_id" : NumberLong(3),
	"provinceId" : NumberLong(7705),
	"cityName" : "深圳",
	"description" : "深圳市一个充满活力的城市",
	"_class" : "com.jack.webflux_mongodb.domain.City"
}
> 


总结:总的来说,主要的还是webflux的知识,在https://blog.csdn.net/j903829182/article/details/80034665文章中是使用map存储的信息,这里改成了mongodb存储数据了,然后使用了docker来运行mongodb的容器。重点还是Flux和Mono,结合不同的数据存储方式保存数据。

源码地址:https://github.com/wj903829182/springcloud5/tree/master/webflux_mongodb



Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐