【微服务架构】-【接口设计】-使用SpringBoot开发Restful服务实现增删改查功能

使用SpringBoot开发Restful服务实现增删改查功能

简介:

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。这篇文章主要介绍了基于SpringBoot开发一个Restful服务,实现增删改查功能,需要的朋友可以参考下

技术与业务:

技术

springboot、restful接口风格

业务

  1. springboot快速构建spring项目开发与部署。

  2. restful风格让API 设计更规范,用于 Web 数据接口的设计,语义更明确

    请求URL对应着请求资源的地址

    操作资源的动词通常就是五种 HTTP 方法,对应 CRUD 操作。

    restful风格的API主要涉及如下设计:

    1. URL设计:API 的 URL,是 五种 HTTP 方法作用的对象。它应该是名词 .

      实际项目中使用动宾短语较多:例如 /getAllCars

    2. 状态码设计:包括 HTTP 状态码和数据两部分

      HTTP 状态码就是一个三位数,分成五个类别。

      • 1xx:相关信息
      • 2xx:操作成功
      • 3xx:重定向
      • 4xx:客户端错误
      • 5xx:服务器错误

      这五大类总共包含100多种状态码,覆盖了绝大部分可能遇到的情况。每一种状态码都有标准的(或者约定的)解释,客户端只需查看状态码,就可以判断出发生了什么情况,所以服务器应该返回尽可能精确的状态码。

  3. 数据传输格式

    请求与响应都为JSON格式

    服务器响应时 API 返回的数据格式,不应该是纯文本,而应该是一个 JSON 对象,因为这样才能返回标准的结构化数据。所以,服务器响应的 HTTP 头的Content-Type属性要设为application/json

    客户端请求时,也要明确告诉服务器,可以接受 JSON 格式,即请求的 HTTP 头的ACCEPT属性也要设成application/json

问题场景说明:

整理下springboot项目restful接口风格代码的编写流程。

博客目录

一、开发准备

  • 1.1 数据库和表
  • 1.2 maven相关依赖

二、工程说明

  • 2.1工程结构图:
  • 2.2 自定义配置文件

三、代码编写

  • 3.1 Pojo类User的编写
  • 3.2 Dao层编写
  • 3.3 Service 业务逻辑层
  • 3.4 Controller 控制层
  • 3.5 Application 主程序

四、代码测试

一、开发准备

在开发程序之前,应先做好一下准备

  1. 1.1 数据库和表
CREATE DATABASE `spring-boot-case`;
USE `spring-boot-case`;
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
 `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
 `name` varchar(10) DEFAULT NULL COMMENT '姓名',
 `age` int(2) DEFAULT NULL COMMENT '年龄',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;
  1. 1.2 maven相关依赖

    springBoot最核心的jar

    spring-boot-starter :核心模块,包括自动配置支持、日志和YAML;

<?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">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>spring-boot-restful-crud</artifactId>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
        <mybatis-spring-boot>1.3.2</mybatis-spring-boot>
        <mysql-connector>8.0.16</mysql-connector>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- Spring Boot Mybatis 依赖 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis-spring-boot}</version>
        </dependency>
        <!-- MySQL 连接驱动依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql-connector}</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <!--运用SpringBoot 插件 使用spring-boot-devtools模块的应用,当classpath中的文件有改变时,会自动重启! -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <fork>true</fork>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>
  1. 2.2 自定义配置文件

    一般我们需要一些自定义的配置,例如配置jdbc的连接配置,在这里我们可以用 application.properties 进行配置。数据源实际的配置以各位的为准。

## 数据源配置
spring.datasource.url=jdbc:mysql://localhost:3307/spring-boot-case?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
## Mybatis 配置
# 配置为 com.dk.bean 指向实体类包路径。
mybatis.typeAliasesPackage=com.dk.bean
# 配置为 classpath 路径下 mapper 包下,* 代表会扫描所有 xml 文件。
mybatis.mapperLocations=classpath\:mapper/*.xml

二、工程说明

2.1工程结构图:

com.dk.web - Controller 层

com.dk.dao - 数据操作层 DAO

com.dk.bean - 实体类

com.dk.bean.service - 业务逻辑层

SpringBootRestfulApplication- 应用启动类

application.properties - 应用配置文件,应用启动会自动读取配置
在这里插入图片描述
2.2 自定义配置文件

一般我们需要一些自定义的配置,例如配置jdbc的连接配置,在这里我们可以用 application.properties 进行配置。数据源实际的配置以各位的为准。

## 数据源配置
spring.datasource.url=jdbc:mysql://localhost:3307/spring-boot-case?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
## Mybatis 配置
# 配置为 com.dk.bean 指向实体类包路径。
mybatis.typeAliasesPackage=com.dk.bean
# 配置为 classpath 路径下 mapper 包下,* 代表会扫描所有 xml 文件。
mybatis.mapperLocations=classpath\:mapper/*.xml

三、代码编写

3.1 Pojo类User的编写
来到重点的代码这快了。

我们开始先编写pojo类,对应数据库中的t_user表。

代码如下

package com.dk.bean;

/**
 * @Description: TODO
 * @Author Cheri
 * @Date 2019/8/4 - 17:11
 * @Version V1.0
 **/
public class User {

    /** 编号 */
    private int id;
    /** 姓名 */
    private String name;

    /** 年龄 */
    private int age;

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

3.2 Dao层编写
在以前的Dao层这块,hibernate和mybatis 都可以使用注解或者使用mapper配置文件。在这
里我们使用ibatis注解来完成CRUD。

说明:

一般有两种方式实现与数据库实现CRUD:

第一种是xml的mapper配置。

第二种是使用注解,@Insert、@Select、@Update、@Delete 这些来完成。本篇使用的是第
二种

package com.dk.dao;

import com.dk.bean.User;
import org.apache.ibatis.annotations.*;
import org.springframework.data.repository.query.Param;

import java.util.List;

/**
 * @Description: TODO
 * @Author Cheri
 * @Date 2019/8/4 - 17:14
 * @Version V1.0
 **/
@Mapper
public interface UserDao {
    /**
     * 用户数据新增
     */
    @Insert("insert into t_user(id,name,age) values (#{id},#{name},#{age})")
    void addUser(User user);
    /**
     * 用户数据修改
     */
    @Update("update t_user set name=#{name},age=#{age} where id=#{id}")
    void updateUser(User user);
    /**
     * 用户数据删除
     */
    @Delete("delete from t_user where id=#{id}")
    void deleteUser(int id);
    /**
     * 根据用户名称查询用户信息
     *
     */
    @Select("SELECT id,name,age FROM t_user")
    // 返回 Map 结果集
    @Results({
            @Result(property = "id", column = "id"),
            @Result(property = "name", column = "name"),
            @Result(property = "age", column = "age"),
    })
    User findByName(@Param("name") String userName);
    /**
     * 根据用户ID查询用户信息
     *
     */
    @Select("SELECT id,name,age FROM t_user")
    User findById(@Param("id") int userId);
    /**
     * 根据用户age查询用户信息列表
     */
    @Select("SELECT id,name,age FROM t_user where age = #{userAge}")
    List<User> findByAge(int userAge);
}

这个接口使用的注解个人理解:

mapper : 在接口上添加了这个注解表示这个接口是基于注解实现的CRUD。

Results: 返回的map结果集,property 表示User类的字段,column 表示对应数据库的字段。

Param:sql条件的字段。

Insert、Select、Update、Delete:对应数据库的增、查、改、删。

3.3 Service 业务逻辑层
这块和hibernate、mybatis的基本一样。

代码如下:

接口

package com.dk.service;

import com.dk.bean.User;

import java.util.List;

/**
 * @Description: TODO
 * @Author Cheri
 * @Date 2019/8/4 - 17:21
 * @Version V1.0
 **/
public interface UserService {
    /**
     * 新增用户
     * @param user
     * @return
     */
    boolean addUser(User user);
    /**
     * 修改用户
     * @param user
     * @return
     */
    boolean updateUser(User user);
    /**
     * 删除用户
     * @param id
     * @return
     */
    boolean deleteUser(int id);
    /**
     * 根据用户名字查询用户信息
     * @param userName
     */
    User findUserByName(String userName);
    /**
     * 根据用户ID查询用户信息
     * @param userId
     */
    User findUserById(int userId);
    /**
     * 根据用户年龄查询用户信息
     * @param userAge
     */
    List<User> findUserByAge(int userAge);
}

实现类

package com.dk.service.impl;

import com.dk.bean.User;
import com.dk.dao.UserDao;
import com.dk.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * @Description: TODO
 * @Author Cheri
 * @Date 2019/8/4 - 17:23
 * @Version V1.0
 **/
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;

    @Transactional
    @Override
    public boolean addUser(User user) {
        boolean flag = false;
        try {
            userDao.addUser(user);
            flag = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return flag;
    }

    @Transactional
    @Override
    public boolean updateUser(User user) {
        boolean flag = false;
        try {
            userDao.updateUser(user);
            flag = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return flag;
    }

    @Transactional
    @Override
    public boolean deleteUser(int id) {
        boolean flag = false;
        try {
            userDao.deleteUser(id);
            flag = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return flag;
    }

    @Override
    public User findUserByName(String userName) {
        return userDao.findByName(userName);
    }

    @Override
    public User findUserById(int userId) {
        return userDao.findById(userId);
    }

    @Override
    public List<User> findUserByAge(int userAge) {
        return userDao.findByAge(userAge);
    }
}

3.4 Controller 控制层
控制层这块和springMVC很像,但是相比而言要简洁不少。

关于控制层的注解个人的理解如下:

RestController:默认类中的方法都会以json的格式返回。

RequestMapping: 接口路径配置。

method : 请求格式。

RequestParam: 请求参数。

具体实现如下:

package com.dk.controller;

import com.dk.bean.User;
import com.dk.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * @Description: TODO
 * @Author Cheri
 * @Date 2019/8/4 - 17:26
 * @Version V1.0
 **/
@RestController
@RequestMapping(value = "/api/user")
public class UserRestController {
    @Autowired
    private UserService userService;
    @RequestMapping(value = "/addUser", method = RequestMethod.POST)
    public boolean addUser(@RequestBody User user) {
        System.out.println("开始新增...");
        return userService.addUser(user);
    }
    @RequestMapping(value = "/updateUser", method = RequestMethod.PUT)
    public boolean updateUser(User user) {
        System.out.println("开始更新...");
        return userService.updateUser(user);
    }
    @RequestMapping(value = "/deleteUser", method = RequestMethod.DELETE)
    public boolean delete(@RequestParam(value = "userName", required = true) int userId) {
        System.out.println("开始删除...");
        return userService.deleteUser(userId);
    }
    @RequestMapping(value = "/userName", method = RequestMethod.GET)
    public User findByUserName(@RequestParam(value = "userName", required = true) String userName) {
        System.out.println("开始查询...");
        return userService.findUserByName(userName);
    }
    @RequestMapping(value = "/userId", method = RequestMethod.GET)
    public User findByUserId(@RequestParam(value = "userId", required = true) int userId) {
        System.out.println("开始查询...");
        return userService.findUserById(userId);
    }
    @RequestMapping(value = "/userAge", method = RequestMethod.GET)
    public List<User> findByUserAge(@RequestParam(value = "userAge", required = true) int userAge) {
        System.out.println("开始查询...");
        return userService.findUserByAge(userAge);
    }
}

3.5 Application 主程序
SpringApplication 则是用于从main方法启动Spring应用的类。

默认,它会执行以下步骤:

1.创建一个合适的ApplicationContext实例 (取决于classpath)。

2.注册一个CommandLinePropertySource,以便将命令行参数作为Spring properties。

3.刷新application context,加载所有单例beans。

4.激活所有CommandLineRunner beans。

直接使用main启动该类,SpringBoot便自动化配置了。

该类的一些注解说明。:

SpringBootApplication:开启组件扫描和自动配置。

MapperScan: mapper 接口类扫描包配置

代码如下:

package com.dk;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @Description: 基于SpringBoot开发一个Restful服务实现增删改查功能
 * @Author Cheri
 * @Date 2019/8/4 - 16:31
 * @Version V1.0
 **/
@SpringBootApplication
@MapperScan("com.dk.dao")
public class SpringBootRestfulApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootRestfulApplication.class,args);
    }

}

四、代码测试

代码编写完之后,我们进行代码的测试。

启动Application 之后,使用postman工具或IDEA restful插件进行接口的测试。

IDEA restful插件测试结果如下:

[外链图片转存失败(img-do2yJMcH-1564919329681)(C:\Users\Cheri\Desktop\小Q书桌-截图\yr.png)]

这里只使用了一个get和post测试,实际方法都测试过了

项目我放到码云上面去了:

https://gitee.com/JavaBigDataStudy/spring-boot-bucket.git

总结

@Description: 基于SpringBoot开发一个Restful服务实现增删改查功能
 * @Author Cheri
 * @Date 2019/8/4 - 16:31
 * @Version V1.0
 **/
@SpringBootApplication
@MapperScan("com.dk.dao")
public class SpringBootRestfulApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootRestfulApplication.class,args);
    }

}

四、代码测试

代码编写完之后,我们进行代码的测试。

启动Application 之后,使用postman工具或IDEA restful插件进行接口的测试。

IDEA restful插件测试结果如下:

在这里插入图片描述
在这里插入图片描述
这里只使用了一个get和post测试,实际方法都测试过了

最后说下:状态码设计
新建base文件夹存放全局基础信息
设计如下两个类封装状态码信息:

package com.dk.base;

/**
 * @author dk
 */
public class RestResponse<T> {
    private int code;
    private String message;
    private T response;

    public RestResponse(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public RestResponse(int code, String message, T response) {
        this.code = code;
        this.message = message;
        this.response = response;
    }

    public static RestResponse ok() {
        SystemCode systemCode = SystemCode.OK;
        return new RestResponse(systemCode.getCode(), systemCode.getMessage());
    }

    public static <F> RestResponse<F> ok(F response) {
        SystemCode systemCode = SystemCode.OK;
        return new RestResponse<>(systemCode.getCode(), systemCode.getMessage(), response);
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getResponse() {
        return response;
    }

    public void setResponse(T response) {
        this.response = response;
    }
}
package com.dk.base;

/**
 * @author dk
 */
public enum SystemCode {
    /**
     * OK
     */
    OK(1, "Ok"),
    /**
     * AccessTokenError
     */
    AccessTokenError(400, "AccessToken Unvalidated"),
    /**
     * UNAUTHORIZED
     */
    UNAUTHORIZED(401, "User UNAUTHORIZED"),
    /**
     * UNAUTHORIZED
     */
    AuthError(402, "Username or Password error"),
    /**
     * InnerError
     */
    InnerError(500, "Api Inner Exception"),
    /**
     * RestFulInnerError
     */
    RestFulInnerError(501, "Web Api Inner Exception");

    int code;
    String message;

    SystemCode(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public int getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }
}

在controller层的UserRestController添加方法

  //状态码设计
    @RequestMapping(value = "/userAge2", method = RequestMethod.GET)
    public RestResponse<List<User>> findByUserAge2(@RequestParam(value = "userAge", required = true) int userAge) {
        System.out.println("开始查询...");
        return RestResponse.ok(userService.findUserByAge(userAge));
    }

完整代码如下:

package com.dk.controller;

import com.dk.base.RestResponse;
import com.dk.bean.User;
import com.dk.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * @Description: TODO
 * @Author Cheri
 * @Date 2019/8/4 - 17:26
 * @Version V1.0
 **/
@RestController
@RequestMapping(value = "/api/user")
public class UserRestController {
    @Autowired
    private UserService userService;
    @RequestMapping(value = "/addUser", method = RequestMethod.POST)
    public boolean addUser(@RequestBody User user) {
        System.out.println("开始新增...");
        return userService.addUser(user);
    }
    @RequestMapping(value = "/updateUser", method = RequestMethod.PUT)
    public boolean updateUser(User user) {
        System.out.println("开始更新...");
        return userService.updateUser(user);
    }
    @RequestMapping(value = "/deleteUser", method = RequestMethod.DELETE)
    public boolean delete(@RequestParam(value = "userName", required = true) int userId) {
        System.out.println("开始删除...");
        return userService.deleteUser(userId);
    }
    @RequestMapping(value = "/userName", method = RequestMethod.GET)
    public User findByUserName(@RequestParam(value = "userName", required = true) String userName) {
        System.out.println("开始查询...");
        return userService.findUserByName(userName);
    }
    @RequestMapping(value = "/userId", method = RequestMethod.GET)
    public User findByUserId(@RequestParam(value = "userId", required = true) int userId) {
        System.out.println("开始查询...");
        return userService.findUserById(userId);
    }
    @RequestMapping(value = "/userAge", method = RequestMethod.GET)
    public List<User> findByUserAge(@RequestParam(value = "userAge", required = true) int userAge) {
        System.out.println("开始查询...");
        return userService.findUserByAge(userAge);
    }

    //状态码设计
    @RequestMapping(value = "/userAge2", method = RequestMethod.GET)
    public RestResponse<List<User>> findByUserAge2(@RequestParam(value = "userAge", required = true) int userAge) {
        System.out.println("开始查询...");
        return RestResponse.ok(userService.findUserByAge(userAge));
    }
}

最后使用restful测试插件测试下:见下图正常返指定格式数据
在这里插入图片描述
状态码设计要结合业务和http规范,大家统一决定即可
项目我放到码云上面去了:

https://gitee.com/JavaBigDataStudy/spring-boot-bucket.git

总结

以上所述介绍的基于SpringBoot开发一个Restful服务实现增删改查功能,又复习一遍restful项目编码开发流程,

发布了234 篇原创文章 · 获赞 12 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/Coder_Boy_/article/details/98473372