SpringBoot整合WebFlux、MongoDB实现增删改

2020-05-10  本文已影响0人  程就人生

WebFlux是异步的、非阻塞的响应式编程,目前支持的数据库有MongoDB和Redis,并不支持MySql,今天就整合MongoDB来实现一套增删改查,感受一下最终的效果。

首先,在pom中引入WebFlux及MongoDB的架包;

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
        </dependency>

第二步,在配置文件中,配置MongoDB的连接,需要在本地安装MongoDB数据库;

spring.data.mongodb.port=27017
spring.data.mongodb.host=localhost
#spring.data.mongodb.username=
#spring.data.mongodb.password=123456
spring.data.mongodb.database=test

第三步,controller、service、dao层代码;

import java.time.Duration;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.example.demo.service.UserService;
import com.example.demo.vo.User;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * controller层
 * @author 程就人生
 * @Date
 */
@Controller
@RequestMapping("/user")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    /**
     * 列表页面
     * @return
     */
    @GetMapping("/userList")
    public String userList(final Model model) {
        //直接将列表渲染到页面
        //final Flux<User> userList = userService.findAll();
        //model.addAttribute("userList", userList);
        return "userList";
    }
    
    /**
     * 新增页面
     * @return
     */
    @GetMapping("/add")
    public String user() {
        return "user";
    }

    /**
     * 保存
     * @param user
     * @return
     */
    @PostMapping
    @ResponseBody
    public Mono<User> save(User user) {
        return userService.save(user);
    }

    /**
     * 通过用户名删除用户,有返回值
     * @param username
     * @return
     */
    @DeleteMapping("/{username}")
    @ResponseBody
    public Mono<Long> deleteByUsername(@PathVariable String username) {
        return userService.deleteByUsername(username);
    }
    
    /**
     * 通过id删除用户,没有返回值
     * @param id
     * @return
     */
    @GetMapping("/delete/{id}")
    @ResponseBody
    public Mono<Void> delete(@PathVariable("id") String id){
        return userService.findById(id).flatMap(x->
        userService.deleteById(x.getId())); 
    }

    /**
     * 通过用户名获取用户
     * Mono返回
     * @param username
     * @return
     */
    @GetMapping("/{username}")
    @ResponseBody
    public Mono<User> findByUsername(@PathVariable String username) {
        //查找不到时返回一个新的User对象
        return userService.findByUsername(username).map(x-> x)
                .defaultIfEmpty(new User());
    }

    /**
     * 第一种方式,数据延迟获取,一次只返回一条,可以减轻带宽压力
     * Flux,返回一次或多次
     * @return
     */
    @GetMapping(value="", produces = MediaType.APPLICATION_STREAM_JSON_VALUE)
    @ResponseBody
    public Flux<User> findAll() {
        //每条数据之间延迟2秒
        return userService.findAll().delayElements(Duration.ofSeconds(2));
    }
    
    /**
     * 第二种方式,返回list<User>集合
     * Flux,返回一次或多次
     * @return
     */
    @GetMapping("/list")
    @ResponseBody
    public Flux<User> list(){
        Flux<User> flux = userService.findAll().map(x->{
            User user = new User();
            BeanUtils.copyProperties(x,user);
            return user;
        });
        return flux;
    }

}

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.example.demo.dao.UserRepository;
import com.example.demo.vo.User;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
 * service层
 * @author 程就人生
 * @Date
 */
@Service
public class UserService {
    
    @Autowired
    private UserRepository userRepository;

    /**
     * 保存或更新。
     * 如果传入的user没有id属性,由于username是unique的,在重复的情况下有可能报错,
     * 这时找到以保存的user记录用传入的user更新它。
     */
    public Mono<User> save(User user) {
        return userRepository.save(user)
                .onErrorResume(e ->     // 进行错误处理
                        userRepository.findByUsername(user.getUsername())   // 找到username重复的记录
                                .flatMap(originalUser -> {      // 由于函数式为User -> Publisher,所以用flatMap
                                    user.setId(originalUser.getId());
                                    return userRepository.save(user);   // 拿到ID从而进行更新而不是创建
                                }));
    }

    public Mono<Long> deleteByUsername(String username) {
        return userRepository.deleteByUsername(username);
    }

    public Mono<User> findByUsername(String username) {
        return userRepository.findByUsername(username);
    }
    
    public Flux<User> findAll() {
        return userRepository.findAll();
    }
    
    public Mono<User> findById(String id){
        return userRepository.findById(id);
    }
    
    public Mono<Void> deleteById(String id) {
        return userRepository.deleteById(id);
    } 
}

import org.springframework.data.repository.reactive.ReactiveCrudRepository;

import com.example.demo.vo.User;

import reactor.core.publisher.Mono;

/**
 * Dao层
 * 继承ReactiveCrudRepository接口
 * @author 程就人生
 * @Date
 */
public interface UserRepository extends ReactiveCrudRepository<User, String> {
    
    //根据名称查找用户对象
    Mono<User> findByUsername(String username);
    
    //根据名称删除用户
    Mono<Long> deleteByUsername(String username);
}

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;

import lombok.Data;

/**
 * user的实体类,使用lombok生成get、set方法
 * @author 程就人生
 * @Date
 */
@Data
public class User {
    
    @Id
    private String id;
    
    //对username做了唯一约束,需要在数据库里做索引
    @Indexed(unique = true)
    private String username;
    
    private String phone;
    
    private String email;

}

第四步,页面代码;
新增页面:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>用户新增</title>
</head>

<body>
<form class="m-t" role="form" id="userForm" th:action="@{/user}" method="post">
<table>
<tr>
    <td>username:</td>
    <td><input type="text" name="username" /></td>
</tr>
<tr>
    <td>phone:</td>
    <td><input type="text" name="phone" /></td>
</tr>
<tr>
    <td>email:</td>
    <td><input type="text" name="email" /></td>
</tr>
</table>
<button type="button" id="addUser" >保存</button>
</form>
</body>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js" ></script>
<script th:inline="javascript" >
    $("#addUser").click(function(){
        $.ajax({
            type: "POST",
            url: "/user",
            data: $("#userForm").serialize(),
            dataType: "json",
            success : function(data) {
                if(data&&data.id){
                    alert("添加成功~!");
                }else{
                    alert("添加失败~!");
                }
                console.log("返回数据:" + data);
           }
       });
    });
</script>
</html>

列表页面:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>用户列表</title>
</head>

<body>
<div>
    <div id="dataModule"></div><br/>
    <div id="note" style="width: 100%;" ></div>
</div>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js" ></script>
<script th:inline="javascript" >
//删除操作
function del(id){
    $.ajax({
        type: "get",
        url: "/user/delete/"+id,
        dataType: "json",
        success : function(data) {
            console.log("数据:" + data);  
       }
   });
}
//删除操作
function delByName(name){
    $.ajax({
        type: "DELETE",
        url: "/user/"+name,
        dataType: "json",
        success : function(data) {
            console.log("数据:" + data);
            if(data=='1'){
                alert("删除成功!");
            }else{
                alert("删除失败!");
            }
       }
   });
}
//根据名称获取对象
function getByName(name){
    $.ajax({
        type: "get",
        url: "/user/"+name,
        dataType: "json",
        success : function(data) {
            console.log("数据:" + data);  
            alert(data.username);
       }
   });
}
var time=1;
$(function() {  
    var xhr = new XMLHttpRequest();
    xhr.open('GET', '/user');
    //xhr.open('GET', '/user/list');
    xhr.send(null);//发送请求
    xhr.onreadystatechange = function() {
        console.log("s响应状态:" + xhr.readyState);
        //2是空响应,3是响应一部分,4是响应完成
        if (xhr.readyState > 2) {
            //这儿可以使用response(对应json)与responseText(对应text)
            var newData = xhr.response.substr(xhr.seenBytes);
            newData = newData.replace(/\n/g, "#");
            newData = newData.substring(0, newData.length - 1);         
            var data = newData.split("#");
            //newData = newData.substring(0, newData.length); //请求user/list时
            if(newData){
                console.log("返回数据:" + newData);
                //var data = JSON.parse(newData);
                //显示加载次数,和大小
                $("#dataModule").append("第"+time+"次数据响应"+data.length+"条<br/>");
                
                $("#note").append("<div style='clear: both;'>第"+time+"次数据响应"+data.length+"条</div><div id='note"+time+"' style='width: 100%;'></div>");
                var html="";
                console.log("数据:" + data);          
                for(var i=0;i<data.length;i++) {
                     var obj = JSON.parse(data[i]);
                     //var obj = data[i];
                     html=html + "<div style='margin-left: 10px;margin-top: 10px; width: 80px;height: 80px;background-color: gray;float: left;'>"+obj.username+","+obj.phone+ "," + obj.email;
                     html=html + "<label onclick='getByName(\""+obj.username+"\")' >获取</label>  <label onclick='del(\""+obj.id+"\")' >删除</label>  <label onclick='delByName(\""+obj.username+"\")' >删除1</label></div>"
                }           
                $("#note"+time).html(html);
                time++;
                xhr.seenBytes = xhr.response.length;
            }
        }
    }   
})
</script>
</body>
</html>

最后,测试;

新增测试
列表获取
单个获取
删除测试

总结
从使用WebFlux实现增删改查的结果来看,对于使用者来说,新增、修改、删除体验不到什么变化,在列表展示时,数据还是一次性从数据库中获取的,然后以较小的流量一点一点地反馈给前台。

上一篇下一篇

猜你喜欢

热点阅读