1. 程式人生 > >springboot2學習-webflux和mongodb

springboot2學習-webflux和mongodb

        這裡學習下webflux和mongodb的結合,使用mongodb來進行資料的儲存,使用docker來啟動mongodb容器簡化開發。docker資料學習可以參考:https://www.jianshu.com/p/f272726db9c5

這篇部落格的學習來源是http://gitbook.cn/gitchat/column/5acda6f6d7966c5ae1086f2b/topic/5acda9f9d7966c5ae108705c

        環境:虛擬機器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
[[email protected] ~]# 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