demo4:在Springboot框架上搭建访问mysql数据库的RESTful接口应用

2,105 阅读8分钟

经历了各种不可控与不可预知的磨难(包括但不限于idea闪退、navicat闪退、系统闪退、莫名其妙的bug无数、疯狂搜索各种注解是为什么无数、jdbcTemplate为什么失效了、@autowired为什么失效了,等等!),终于搞定了这个主题!!!!

本篇就来手把手教你搞定这个应用!


1. 大致思路

首先,大致总结一下,如果你想要从零开始,利用springboot框架,写一个能访问数据库并进行crud操作的,能给RESTful风格的接口的应用,应该怎么做:

  1. 安装一个mysql环境,不会/不喜欢使用命令行的话,需要再装一个数据库软件,例如navicat或者mysqlworkbench,本文以navicat来说明
  2. 建数据库、数据表、可访问数据库的用户名+密码(最好不要直接给root权限)
  3. 推荐:装一个postman,方便请求接口查看结果
  4. 初始化一套springboot的框架,装上我们需要的插件依赖(后续会提到)
  5. application.properties文件中,可以配置上连接数据库需要用的信息,也可以配置上想要应用启动所用的端口号
  6. 写三个java类:controllerentityservice
  • entity:用于写数据表对应的字段在java里面对应的对象,需要具有Bean的特征:getter、setter等;
  • service:用于写mysql语句与对应的jdbc请求
  • controller:用于调用service,制作对应的get/post接口
  1. 启动运行整个app

2. 建mysql数据库

懂得怎么搞这玩意的可以跳过这一part~~~

另外,此处不讨论如何本地搭一个docker,并在容器里面安装mysql

2.1 下载一个适合你的电脑版本的mysql

参考教程:blog.csdn.net/baidu_26315…

2.2 下载一个mysql软件

免费:mysqlWorkbench 付费:navicat

2.3 建库、表、数据、用户

2.3.1 启动mysql

image.png

image.png

2.3.2 navicat连接数据库

image.png

2.3.3 新建数据库 spring_example

image.png

2.3.4 新建数据表 user

image.png

image.png

2.3.5 填充数据

image.png

2.3.6 新建用户,用于spring访问该数据库

选择新建用户 image.png

添加常规信息,设置用户的名称和密码 image.png

添加用户的对象权限 image.png

勾选所需要的数据库的权限 image.png

3. 创建springboot工程

3.1 初始化一个springboot项目

利用官网生成一个springboot项目:start.spring.io/

所需要的依赖:

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

   <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <scope>runtime</scope>
   </dependency>
   <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
   </dependency>
   <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-jdbc</artifactId>
   </dependency>
   <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
   </dependency>
   <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.3.13</version>
      <scope>compile</scope>
   </dependency>
</dependencies>

重点依赖说明:

  • lombok:是一个可以简化代码的库,可以帮助你不用自己去写getter()或者setter(),就能自动在调试编译的时候,生成Bean的相关方法。可以参考这篇文章来理解:# 整合Lombok简化接口对象代码

  • spring-boot-starter-web:用于创建springboot的web服务,可以提供REST风格相关api

  • mysql-connector-javaspring-boot-starter-jdbc:提供了mysql的JDBC连接方法,尤其是JdbcTemplate

然后,我们还需要配置这个工程项目和我们创建的数据库的连接信息:

image.png

# mysql connection settings
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/spring_example
spring.datasource.username=spring
spring.datasource.password=spring
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

3.2 新建相关的package和java class

image.png

正如本篇开头所提到的,按照规范来说,java代码应该分好层次,所以我们会建立以上所示的三个package,分别代表以下含义:

  • entity:用于写数据表对应的字段在java里面对应的对象,需要具有Bean的特征:getter、setter等;
  • service:用于写mysql语句与对应的jdbc请求
  • controller:用于调用service,制作对应的get/post接口

然后分别对应创建相应的class,下面我们会分别讲解:

3.2.1 user.class

首先,我们要创建一个对应了数据表字段内容的类,便于我们从数据库取得数据后,可以存放在实例化的对象中。

常规来说,我们需要声明了一堆private的变量,然后写一些公共的getter()、setter()方法,来实现一个标准的java bean,便于我们在其他方法中实例化这个类型的对象。

但是,下面的代码就说明了,我们可以轻轻松松的用lombok的方法,添加@Data注解,让自己不用再写一长串getter()、setter(),而让代码在编译的时候给我们自动生成这些东西。

package shenling.example.springbootJDBC.entity;

import lombok.Data;

@Data
public class user {
    private Integer id;
    private String firstname;
    private String lastname;
}

要注意的是,如果要让lombok生效,还需要让idea中下载并开启插件:lombok

image.png

3.2.2 userService.class

在service层中,我们提供针对这个表的JDBC连接服务。基本上下面代码的逻辑就是:写一个sql,用JdbcTemplate来进行数据库连接和请求。

特别指出:相比起常规的普通框架,这里面由于我们使用了JdbcTemplate这些方法,所以不用再考虑要做database.connection这些“连接数据库 - crud - 关闭连接”这些额外的操作了,让我们更为集中精力在业务逻辑的实现上。这也是springboot的一大特色。

package shenling.example.springbootJDBC.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import shenling.example.springbootJDBC.entity.user;

import java.util.List;

@Service
@Component
public class userService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    // 查询列表
    public List<user> getList() {
        String sql = "SELECT * FROM user";
        List<user> result = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(user.class));
        return result;
    }


    // 新增
    public int addUser(user newUser) {
        String sql = "INSERT INTO user(id, firstname, lastname)values(?,?,?)";
        return jdbcTemplate.update(sql, newUser.getId(), newUser.getFirstname(), newUser.getLastname());
    }

    // 更新
    public int updateUser(user newUser) {
        String sql = "update user set firstname = ?, lastname=?  where id = ?";
        return jdbcTemplate.update(sql, newUser.getFirstname(), newUser.getLastname(), newUser.getId());
    }

    // 删除
    public int deleteUser(int id) {
        String sql = "delete from user where id = ?";
        return jdbcTemplate.update(sql, id);
    }

}

要注意几个点:

  1. 我们引入了JdbcTemplate的对象,来使用相关的方法,对数据库进行sql查询请求,完成crud操作。这里可以参考学习这篇文章:www.jianshu.com/p/f0cbed671…

  2. 在springboot里面,只要用@Component注解过的class,就不用在调用的时候使用new进行实例化对象了,而是用@Autowired注解搞定。所以:

  • JdbcTemplate变量声明的前面添加@Autowired注解
  • userService类,要在controller那边通过@Autowired使用,所以这个类的前面需要添加@Component注解
  1. query的时候,一般来说,使用的是List<Map<T, P>>的方法来保存结果,query的第二个参数也是用的这种map类。但是根据一些文的讨论和推荐,更为建议使用RowMapper的方式保存数据结果,认为速度更快更优。

3.2.3 userController.class

在controller里面,便是要创建这些接口了。就可以参考前面的关于如何建接口的demo来理解。

package shenling.example.springbootJDBC.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.web.bind.annotation.*;
import shenling.example.springbootJDBC.entity.response;
import shenling.example.springbootJDBC.entity.user;
import shenling.example.springbootJDBC.service.userService;

import java.util.List;

@RestController
@RequestMapping("/user")
public class userController {

    @Autowired
    private userService userService;

    @Autowired
    private response res;


    @GetMapping("/list")
    public @ResponseBody response getUserList(){
        try {
            List<user> result = userService.getList();

            res.setResult(result);
            res.setCode(10000);
            res.setMsg("查询成功");

        }
        catch(DataAccessException e) {
            e.printStackTrace();

            String exceptionMsg = e.getRootCause().getMessage();
            System.out.println("ERROR:" + exceptionMsg);
            res.setCode(500);
            res.setMsg(exceptionMsg);
        }

        return res;
    }

    @PostMapping("/add")
    public @ResponseBody response addUser(user newUser) {
        try {
            Integer result = userService.addUser(newUser);

            res.setResult(newUser);
            res.setCode(10000);
            res.setMsg("新增成功");

        }
        catch(DataAccessException e) {
            e.printStackTrace();

            String exceptionMsg = e.getRootCause().getMessage();
            System.out.println("ERROR:" + exceptionMsg);
            res.setCode(500);
            res.setMsg(exceptionMsg);
        }

        return res;
    }

    @PostMapping("/update")
    public @ResponseBody response updateUser(user newUser) {
        try {
            Integer result = userService.updateUser(newUser);

            res.setResult(newUser);
            res.setCode(10000);
            res.setMsg("更新成功");

        }
        catch(DataAccessException e) {
            e.printStackTrace();

            String exceptionMsg = e.getRootCause().getMessage();
            System.out.println("ERROR:" + exceptionMsg);
            res.setCode(500);
            res.setMsg(exceptionMsg);
        }

        return res;
    }

    @PostMapping("/delete")
    public @ResponseBody response deleteUser(int id) {
        try {
            Integer result = userService.deleteUser(id);

            res.setResult(id);
            res.setCode(10000);
            res.setMsg("删除成功");

        }
        catch(DataAccessException e) {
            e.printStackTrace();

            String exceptionMsg = e.getRootCause().getMessage();
            System.out.println("ERROR:" + exceptionMsg);
            res.setCode(500);
            res.setMsg(exceptionMsg);
        }

        return res;
    }
}

同样的,我们可以在这里面看到几个关键点:

  1. @RestController:要制作成接口,那么类前面就肯定要写上这个注解才行

  2. @RequestMapping("/user"):由于我们想要让接口访问的时候,第一段是user,然后才是后面的请求list、新增用户add等,所以要在类前面写上这个。当然如果不需要这一段跳转的话,可以不写。

  3. @Autowired:我们前面的userService那里用了@Component注解,所以这里就可以直接用autowired来进行实例化,而不用通过new了

  4. 这段代码中,可以看到,我们通过userService.getList()就可以拿到查询后的结果,并且可以通过抛出异常DataAccessException抓取到异常日志。但是为了规范化我们的输出结果为msg/code/result的样式,所以我们还需要建一个response的类来声明我们的结果,下一小节会进行说明。

  5. @ResponseBody:这个写在了方法那里,是为了让方法返回的结果是JSON样式输出的,具有一种格式化、标准化的作用,来实现非常典型的rest接口返回结果风格。

3.2.4 response.class

为了让我们的输出结果标准化,所以在entity的package中,我们再建了一个class,去保存我们的输出结果的样子:

package shenling.example.springbootJDBC.entity;

import lombok.Data;
import org.springframework.stereotype.Component;

@Data
@Component
public class response {
    private String msg;
    private Integer code;
    private Object result;
}

这里面我们可以看到:

  1. 用了@Data来实现一个Bean的风格的类

  2. 用了@Component来保证在controller中,可以通过autowired进行使用

3.3 创建运行这个应用的class

一般来说,通过官网初始化的springboot应用里面就包含了这个部分了:

package shenling.example.springbootJDBC;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


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

}

其中,@SpringBootApplication注解就能让这个应用运行的时候去找到我们的controller

4. 运行并查看结果

4.1 默认的8080端口已经被占用的问题:

有时候,默认8080端口被占用,所以我们可以查看这个端口号的pid并杀掉这个进程 image.png

当然也可以在application.property中设置一个新的端口号,例如:

server.port=1234

4.2 控制台结果

运行成功后,控制台会显示当前web运行的端口号 image.png

4.3 用postman发起请求进行查看

当然,我们完全可以用浏览器直接访问get类型的接口,但是为了显得很专业+直观+美观,我们用postman这种接口管理工具

  1. 下载本地postman软件:www.postman.com/downloads/?…

  2. 发送请求并查看结果:

image.png

5. 要点小结

1. 推荐使用lombok库,非常方便开发,减少你的代码行数,但是在build的时候需要忽略掉,因为编译会自动生成这个库的

image.png

2. 使用@autowired的时候要注意这个被声明的类,需要有@Component的注解

@Component
public class FirstClass {
    ...
}

public class SecondClass {
    @Autowired
    FirstClass f;
}

3. 要知道controllerentityservice的区别,分别应该写什么内容(有些框架要求写的是dao层这些)

4. 推荐创建标准的接口输出模板,并抓到请求异常msg进行抛出

5. 在写接口的controller的类前面,一定要写上@RestController

@RestController
public class controller {
    ...
}

6. 要注意阅读控制台的报错信息,对应去查bug