当前位置:   article > 正文

SpringBoot开发流程

springboot开发流程

1、新建项目

Spring Web + MySQL Driver + Dev Tools添加后三个选项

修改配置文件为yml格式

端口设置为80 方便访问

手动导入Mybatis Plus 和 Druid连接池

这里遇到一个问题,就是修改MP或druid版本号会报错?

2、创建数据库表对应的实体类

开发实体类

@Data替代了get/set方法,toString方法,hasCode方法,equals方法等

3、数据层开发(dao层)

主流的数据层技术:Mybatis、MP(Mybatis Plus)、Hibernate

(1)导入MP和Druid对应的依赖(第一步已完成)

完成druid的配置:

yml中输入data然后提示中选择driver-class-name,然后前面添加druid即可

然后再对MP的表前缀进行配置(Mybatis plus完成配置):

  1. mybatis-plus:
  2. global-config:
  3. db-config:
  4. table-prefix: tbl_

(2)然后创建数据层接口:

New新建 dao.BookDao接口

  1. @Mapper
  2. public interface BookDao extends BaseMapper<Book> {
  3. }
  4. //泛型 填写对应的实体类

 然后在Test文件夹下,建立相应dao的测试类,类上添加注解:@SpringBootTest

New dao.BookDaoTestCase

在这个测试类中测试了CRUD:

  1. import com.bo.pojo.Book;
  2. import lombok.AllArgsConstructor;
  3. import org.junit.jupiter.api.Test;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.boot.test.context.SpringBootTest;
  6. /**
  7. *
  8. * @date 2022/4/26 - 14:34
  9. */
  10. @SpringBootTest
  11. public class BookDaoTestCase {
  12. @Autowired
  13. private BookDao bookDao;
  14. //查询操作
  15. //根据id查询
  16. @Test
  17. void testGetById(){
  18. System.out.println(bookDao.selectById(2));
  19. }
  20. //增加记录
  21. //不设置id字段会报错,mybatis系统异常,MP默认生成id策略由他自己设置,雪花算法
  22. //我们想要使用表的自增策略,需要在yml中配置 id-type: auto
  23. @Test
  24. void testSave(){
  25. Book book = new Book();
  26. book.setType("测试数据123");
  27. book.setName("测试数据123");
  28. book.setDescription("测试数据123");
  29. bookDao.insert(book);
  30. }
  31. //更新操作,修改操作
  32. @Test
  33. void testUpdate(){
  34. Book book = new Book();
  35. book.setId(2);
  36. book.setType("测试数据Update");
  37. book.setName("测试数据Update");
  38. book.setDescription("测试数据Update");
  39. bookDao.updateById(book);
  40. }
  41. //删除
  42. @Test
  43. void testDelete(){
  44. bookDao.deleteById(2);
  45. }
  46. //查询所有的数据
  47. //所有查询都以select开头
  48. @Test
  49. void testGetAll(){
  50. System.out.println(bookDao.selectList(null));
  51. }
  52. }

(3)在yml文件中开启MP运行日志:

  1. mybatis-plus:
  2. global-config:
  3. db-config:
  4. table-prefix: tbl_
  5. id-type: auto
  6. configuration:
  7. log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

辅助debug,服务器上线时不要开启!!!

(4)实现分页查询

IPage是一个接口,Page是它的实现类,新建IPage接口对象,第一个参数为第几页(从1开始),第二个参数介绍每页有多少行记录(从1开始)

page对象中封装了分页操作中的所有数据:

数据、当前页码、每页数据总量、最大页码值、数据总量

最主要的是添加MP的拦截器!!!才能实现分页功能(也就是为sql语句拼接上limit)

  1. @Test
  2. void testGetPage(){
  3. //IPage是一个接口
  4. //哪一页的数据,一页显示多少数据(1,5) 第一页,一页显示5条
  5. //分页功能实现 select * from tbl_book limit ?.?
  6. //分页若想使用,必须使用拦截器实现
  7. //返回值还是IPage对象 page
  8. //返回的数据存在了page对象内
  9. IPage page = new Page(2,5);
  10. bookDao.selectPage(page,null);
  11. //需要调用page对象的方法取出
  12. System.out.println(page.getCurrent());
  13. System.out.println(page.getSize());
  14. System.out.println(page.getTotal());
  15. System.out.println(page.getPages());
  16. System.out.println(page.getRecords());
  17. }

 MP的分页拦截器:

New config.MPConfig

功能就是为SQL语句拼接部分内容 limit ....

  1. @Configuration
  2. public class MPConfig {
  3. //交给spring一个bean
  4. //bean是MP的拦截器
  5. //这个拦截器里面里面添加了PaginationInnerInterceptor,添加了一个分页的子拦截器
  6. @Bean
  7. public MybatisPlusInterceptor mybatisPlusInterceptor(){
  8. MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
  9. interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
  10. return interceptor;
  11. }
  12. }

 (4)添加按条件查询的功能

上面的所有方法都可以添加查询条件!

  1. @Test
  2. void testGetBy(){
  3. //QueryWrapper<>就是查询条件 泛型可写可不写QueryWrapper与QueryWrapper<>均可
  4. //设置条件
  5. //like 包括 就是sql中的like
  6. //eq 等于
  7. //ne 不等于
  8. //lt 小于
  9. //...
  10. // QueryWrapper<Book> qw = new QueryWrapper<Book>();
  11. //问题一:
  12. // "name"是手写的,假如写错了怎么解决,属性容易写错
  13. // qw.like("name","Spring"); name写错了会出错
  14. //解决方法:
  15. // qw.like(Book::getName(),name);
  16. // 调用Book的getName方法
  17. //问题二:
  18. //如果name为null,会直接把null当作"null"拼接
  19. //方法一:if判断再连接 if(name!=null) lqw.like(Book::getName,name);
  20. //方法二:lqw.like(name!=null,Book::getName,name);
  21. String name = null;
  22. LambdaQueryWrapper<Book> lqw = new LambdaQueryWrapper<Book>();
  23. lqw.like(name!=null,Book::getName,name);
  24. bookDao.selectList(lqw);
  25. }

4、业务层开发(service层)

业务层接口关注的是业务的名称,login(String username, String password)适合业务层的登录业务,而相同功能的selectByUserNamePassword(String username, String password)适合数据层接口。

如果是业务方法,要定义为业务名,如果只是一般的增删改查,就写增删改查即可。

创建业务层接口-->创建业务层实现类-->在实现的方法调用dao中的方法(而该方法又是BaseMapper定义好的)

New service.BookService 接口

  1. /**
  2. * @date 2022/4/26 - 16:35
  3. */
  4. //业务方法就叫业务名login()
  5. //一些操作就叫操作名
  6. public interface BookService {
  7. Boolean save(Book book);
  8. Boolean update(Book book);
  9. Boolean delete(Integer id);
  10. Book getById(Integer id);
  11. List<Book> getAll();
  12. IPage<Book> getPage(int currentPage, int pageSize);
  13. }

定义完业务层接口,接着创建实现类,在service包下新建impl包,在该包下面:

New BookServiceImpl类

  1. /**
  2. * @date 2022/4/26 - 16:39
  3. */
  4. @Service //定义为业务层对应的bean
  5. public class BookServiceImpl implements BookService {
  6. @Autowired
  7. private BookDao bookDao;
  8. @Override
  9. public Boolean save(Book book) {
  10. //大于0操作成功
  11. //小于=0操作失败
  12. return bookDao.insert(book) > 0;
  13. }
  14. @Override
  15. public Boolean update(Book book) {
  16. return bookDao.updateById(book) > 0;
  17. }
  18. @Override
  19. public Boolean delete(Integer id) {
  20. return bookDao.deleteById(id) > 0;
  21. }
  22. @Override
  23. public Book getById(Integer id) {
  24. return bookDao.selectById(id);
  25. }
  26. @Override
  27. public List<Book> getAll() {
  28. return bookDao.selectList(null);
  29. }
  30. @Override
  31. public IPage<Book> getPage(int currentPage, int pageSize) {
  32. IPage page = new Page(currentPage,pageSize);
  33. return bookDao.selectPage(page,null);
  34. }

下面进行测试:

Test文件夹下 New service.BookServiceTestCase

  1. /**
  2. * @date 2022/4/26 - 16:44
  3. */
  4. @SpringBootTest
  5. public class BookServiceTestCase {
  6. @Autowired
  7. private BookService bookService;
  8. @Test
  9. void getById(){
  10. System.out.println(bookService.getById(4));
  11. }
  12. @Test
  13. void testSave(){
  14. Book book = new Book();
  15. book.setType("测试数据123");
  16. book.setName("测试数据123");
  17. book.setDescription("测试数据123");
  18. System.out.println(bookService.save(book));
  19. }
  20. //更新操作
  21. @Test
  22. void testUpdate(){
  23. Book book = new Book();
  24. book.setId(3);
  25. book.setType("id2AAAA");
  26. book.setName("测试数据Update");
  27. book.setDescription("测试数据Update");
  28. System.out.println(bookService.update(book));
  29. }
  30. //删除
  31. @Test
  32. void testDelete(){
  33. System.out.println(bookService.delete(3));
  34. }
  35. //查询所有的数据
  36. //所有查询都以select开头
  37. @Test
  38. void testGetAll(){
  39. System.out.println(bookService.getAll());
  40. }
  41. @Test
  42. void testGetPage(){
  43. IPage<Book> page = bookService.getPage(2, 5);
  44. //需要调用page对象的方法取出
  45. System.out.println(page.getCurrent());
  46. System.out.println(page.getSize());
  47. System.out.println(page.getTotal());
  48. System.out.println(page.getPages());
  49. System.out.println(page.getRecords());
  50. }
  51. }

上面的步骤过于繁琐,如何简化?步骤如下

快速开发方案:使用MyBatisPlus提供业务层通用接口(IService<T>)与业务层通用实现类(ServiceImpl<M,T>),在通用类基础上做功能重载或功能追加,注意重载时不要覆盖原始操作,避免原始提供的功能丢失。

第一步--创建service接口,继承IServic<xxx>  xxx填写对应的实体类

 第二步--实现类定义

5、表现层开发(Controller层)

在该层完成表现层处理:

  1. @RestController
  2. @RequestMapping("/books")
  3. public class BookController {
  4. @Autowired
  5. private IBookService iBookService;
  6. @GetMapping
  7. public List<Book> getAll(){
  8. return iBookService.list();
  9. }
  10. //传的是json数据
  11. @PostMapping
  12. public Boolean save(@RequestBody Book book){
  13. return iBookService.save(book);
  14. }
  15. @PutMapping
  16. public Boolean update(@RequestBody Book book){
  17. return iBookService.modify(book);
  18. }
  19. @DeleteMapping("{id}")
  20. public Boolean delete(@PathVariable Integer id){
  21. return iBookService.removeById(id);
  22. }
  23. @GetMapping("{id}")
  24. public Book getById(@PathVariable Integer id){
  25. return iBookService.getById(id);
  26. }
  27. @GetMapping("{currentPage}/{pageSize}")
  28. public IPage<Book> getPage(@PathVariable int currentPage, @PathVariable int pageSize){
  29. return iBookService.getPage(currentPage,pageSize);
  30. }
  31. }

但是返回给前端的数据格式多种多样,前端处理起来很麻烦,所以需要统一一下后端的返回格式:

这里在utils包下面新增R类(Result),作为我们返回值的统一格式;

然后对Controller做修改:

  1. @RestController
  2. @RequestMapping("/books")
  3. public class BookController2 {
  4. @Autowired
  5. private IBookService iBookService;
  6. @GetMapping
  7. public R getAll(){
  8. return new R(true,iBookService.list());
  9. }
  10. //传的是json数据
  11. @PostMapping
  12. public R save(@RequestBody Book book){
  13. return new R(iBookService.save(book));
  14. }
  15. @PutMapping
  16. public R update(@RequestBody Book book){
  17. return new R(iBookService.modify(book));
  18. }
  19. @DeleteMapping("{id}")
  20. public R delete(@PathVariable Integer id){
  21. return new R(iBookService.removeById(id));
  22. }
  23. @GetMapping("{id}")
  24. public R getById(@PathVariable Integer id){
  25. return new R(true, iBookService.getById(id));
  26. }
  27. @GetMapping("{currentPage}/{pageSize}")
  28. public R getPage(@PathVariable int currentPage, @PathVariable int pageSize){
  29. return new R(true,iBookService.getPage(currentPage,pageSize));
  30. }
  31. }

6、前后端结合

前后端分离结构设计中页面归属前端服务器,这里暂时采用了单体工程

单体工程中页面放置在resour目录下的static目录中(建议执行clean)

 在vue的数据模型中,dataList存储当前页要展示的列表数据;下面两个控制表单是否可见,formData用来记录表单数据,修改和新增用同一个表单数据。

导入前端页面后,然后在vue页面中利用axios发起异步请求,这里测试能否发起一个请求,在钩子函数created()中调用getAll()方法,刷新页面时自动执行。axios.xxx()发送请求,then接受数据后做处理。

查询全部数据并展示:

 

实现新增功能:

首先要弹出添加对话框,同时还要清除formData里面的旧数据

 然后,点击确定之后,提交表单数据到后台,实现添加功能(当前操作失败的时候,不要关闭对话框):

 点击取消按钮,会关闭添加或编辑窗口:

 实现删除操作(then确认,catch取消):

修改操作:

相当于列表功能+新增功能组合,首先要弹出编辑窗口,并且要将准备修改的数据展示出来:

然后修改之后,提交到后台接口,确定提交:

异常消息处理:

如果后台出现异常,则会产生另一种消息格式给前端:

尽管代码出异常,我们也要返回给前端统一的格式,在springMVC中提供的专用的异常处理器:

可以使用ControllerAdvice注解或者RestControllerAdvice注解;

运行处异常之后,被异常拦截器拦截,然后返回一个R对象,所以修改R对象,添加一个msg消息:

 R.class

  1. @Data
  2. public class R {
  3. private Boolean flag;
  4. private Object data;
  5. private String msg;
  6. public R(){}
  7. public R(Boolean flag){
  8. this.flag = flag;
  9. }
  10. public R(Boolean flag, Object data) {
  11. this.flag = flag;
  12. this.data = data;
  13. }
  14. public R(Boolean flag, String msg) {
  15. this.flag = flag;
  16. this.msg = msg;
  17. }
  18. public R(String msg) {
  19. this.flag = false;
  20. this.msg = msg;
  21. }
  22. }

 现在页面上的消息,有在页面写的,还有在代码上写的,所以需要统一在后端添加:

 现在所有的消息统一由后台处理,保存消息前端展示,前端使用res.data.msg使用即可,方便后面进行国际化。

分页功能:

 直接在getAll()方法里面改:

 点击修改页码,响应换页操作:

某一页只有最后一条数据,删除之后依然在本页面,如何解决?如果要查看页码大于总页码值,重新执行查询操作,使用最大页码值作为当前页码值,修改Controller:

按条件查询:

在执行分页的时候这些条件都得带走:

这几个数据在哪?

这三个数据是跟着分页走的,每次分页查询这三个条件都得带走,所以绑定在分页数据模型中:

修改分页查询操作,

 修改Controller接口,但是缺少getPage(currentPage,pageSize, book)接口,所以需要添加

 

 修改service实现类:

 对于条件查询,把它作为分页中的一部分数据,当前页码值和size是条件,type和name等也是查询条件。请求的路径参数,直接写参数(对应名称)就能接,如果有对应的实体类,直接封装为实体类。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/57574
推荐阅读
相关标签
  

闽ICP备14008679号