使用 Spring Boot 和 MongoDB 构建 REST 服务
- MongoDB
- 2019-08-17
- 250热度
- 0评论
导航
以学生信息管理(增删改查)为例。主要目的是,通过开发这样一个小业务功能,熟悉 Spring Boot 的基本用法。
github 链接:https://github.com/plough/student-information-system
技术点概要:
- 用 Spring Boot 和 Spring Data 开发 REST API;
- 采用领域驱动开发(domain-driven development)方式,将分出 model, repository, service, controller 层;
- 相关工具:使用 IntelliJ IDEA 做开发,MongoDB 做持久化,Postman 做接口测试,Gradle 做构建。
(MongoDB、Gradle 等工具的安装和配置略过不讲)
0 业务需求定义:学生信息管理服务
提供一组 REST API,实现对学生信息的增删改查。
具体要求如下:
- 发送 POST 请求,向系统中添加学生;
- 发送 GET 请求,根据学生编号或 email 查询学生信息;
- 发送 GET 请求,查询所有学生信息,并按 GPA 排序;
- MongoDB 中的学生集合,包含以下数据:名字、学生编号、email、课程列表、GPA;
- 存储、接收及返回,都是 JSON 格式。
1 使用 Spring Initialzr 初始化项目
我们使用 Spring Initalizr 来创建一个新的 Spring Boot 项目。
- 选择 Gradle Project,Java,Spring Boot 2.1.4;
- 填写合适的 group 和 artifact 名称;
- 添加依赖:Spring Data MongoDB 和 Spring Web Starter;
截图如下:
点击 Generate the project 按钮,会下载一个 zip 包,我们解压后导入 IDEA。
Gradle 把依赖处理好后,我们把包名改得规范一点。
2 Model 和 Repository
2.1 学生 Model
我们首先定义一个学生模型。
package me.baimoz.demo.student.information.system.model; // imports @Document(collection = "students") public class Student { @Id private String id; private String name; private long studentNumber; private String email; private List<String> courseList; private float gpa; // 构造函数以及 Getters 和 Setters }
@Document 标识了一个需要持久化到 MongoDB 的领域对象。详见文档(IDEA 中直接跳转到源码查看)。
2.2 学生 Repository
得益于 Spring Data 项目,我们只需要创建一个继承自 MongoRepository 的接口,不用实现它。
package me.baimoz.demo.student.information.system.repository; // imports public interface StudentRepository extends MongoRepository<Student, String> { Student findByStudentNumber(long studentNumber); Student findByEmail(String email); List<Student> findAllByOrderByGpaDesc(); }
StudentRepository 提供了我们需要的三种查询方式。
3 学生 Service
现在来创建一个给 controller 调用的 service 对象。
package me.baimoz.demo.student.information.system.service; // imports public interface StudentService { List<Student> findAll(); Student findByStudentNumber(long studentNumber); Student findByEmail(String email); List<Student> findAllByOrderByGpaDesc(); Student saveOrUpdateStudent(Student student); void deleteStudentById(String id); }
通常,controller 可以直接跟 repository 类交互。但是为了避免在 controller 中处理太多业务逻辑,我们需要把业务逻辑封装到 service 类中,因此多了一个中间层。
package me.baimoz.demo.student.information.system.service.impl; // imports @Service public class StudentServiceImpl implements StudentService { @Autowired private StudentRepository studentRepository; @Override public List<Student> findAll() { return studentRepository.findAll(); } @Override public Student findByStudentNumber(long studentNumber) { return studentRepository.findByStudentNumber(studentNumber); } @Override public Student findByEmail(String email) { return studentRepository.findByEmail(email); } @Override public List<Student> findAllByOrderByGpaDesc() { return studentRepository.findAllByOrderByGpaDesc(); } @Override public Student saveOrUpdateStudent(Student student) { return studentRepository.save(student); } @Override public void deleteStudentById(String id) { studentRepository.deleteById(id); } }
4 REST Controller
最后,我们来创建一个 REST 控制器。
package me.baimoz.demo.student.information.system.controller; // imports @RestController @RequestMapping("/students") public class StudentRestController { @Autowired private StudentService studentService; @GetMapping(value = "/") public List<Student> getAllStudents(@RequestParam(value = "orderByGpa", defaultValue = "true") Boolean orderByGpa) { if (orderByGpa) { return studentService.findAllByOrderByGpaDesc(); } return studentService.findAll(); } @GetMapping(value = "/byStudentNumber/{studentNumber}") public Student getStudentByStudentNumber(@PathVariable("studentNumber") Long studentNumber) { return studentService.findByStudentNumber(studentNumber); } @GetMapping(value = "/byEmail/{email}") public Student getStudentByEmail(@PathVariable("email") String email) { return studentService.findByEmail(email); } @PostMapping(value = "/save") public ResponseEntity<?> saveOrUpdateStudent(@RequestBody Student student) { studentService.saveOrUpdateStudent(student); return new ResponseEntity<>("Student added successfully", HttpStatus.OK); } @DeleteMapping(value = "/delete/{studentNumber}") public ResponseEntity<?> deleteStudentByStudentNumber(@PathVariable long studentNumber) { studentService.deleteStudentById(studentService.findByStudentNumber(studentNumber).getId()); return new ResponseEntity<>("Student deleted successfully", HttpStatus.OK); } }
5 数据库配置
在 application.properties 文件中,添加数据库相关的配置。例如:
#server server.port=8081 #mongodb spring.data.mongodb.host=localhost spring.data.mongodb.port=27017 spring.data.mongodb.database=test
6 运行测试
启动 MongoDB 服务器,运行 StudentInformationSystemApplication。
然后就可以用 Postman 来测试我们写的接口了。(代码量这么少就能运行,我感觉很神奇)
具体测试过程略过不表,可参考文末的英文文档。
单元测试略。
参考文章:Building a REST Service with Spring Boot and MongoDB (Part 1)
StudentRepository 真的很神奇,尤其是 findAllByOrderByGpaDesc 方法。Spring 是怎么知道具体实现步骤的?靠方法名推测么?