《程序猿入职必会(3) · SpringBoot 各层功能完善 》

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗
🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数,欢迎多多交流。👍

CSDN.gif

写在前面的话

第一篇博文《程序猿入职必会(1) · 搭建拥有数据交互的 SpringBoot 》,我们已完成了后端服务的搭建,开发并且对外提供了查询教师列表的数据接口,但要按照一开始制定的需求目标,要完整的教师CURD功能,现有接口方法还是远远不够的(まだまだだね)。
本篇博文,有几个目标要完成,

  • 回顾一下之前两篇文章,一些知识点补充一下
  • 针对需求内容,把后端服务的接口方法丰富起来
  • 适当考虑一下代码的封装,将一些共性的方法抽取到基类中,方便后续开发
  • 功能完善过程中,分享一些实战开发中的后端经验

加油,程序猿,保持住Tempo,开干,玩的就是真实!

关联文章:
《程序猿入职必会(1) · 搭建拥有数据交互的 SpringBoot 》
《程序猿入职必会(2) · 搭建具备前端展示效果的 Vue》


前文回顾

前面两篇博文,分别介绍了如何快速创建前端和后端项目,以此之上去开发功能完成需求。
这下有点时间,我们可以回过头来,先总结一些内容,再继续需求。

前后端分离

Tips:很明显,前两篇我们就是基于前后端分离模式开发,具体是个什么样的模式,这边简单介绍一下。

前后端分离是目前互联网开发中比较广泛使用的开发模式,主要是将前端和后端的功能开发进行分离,使得前端和后端可以独立开发、测试和部署。在前后端分离开发中,前端负责展示数据和用户交互,后端负责处理业务逻辑和数据存储。 具体来说,前端开发人员使用 HTML、CSS、JavaScript 等技术实现用户界面和交互逻辑,通过 AJAX 或 WebSocket 等技术与后端进行通信,获取数据并将数据展示给用户。后端开发人员则负责编写业务逻辑和数据存储的代码,提供 API 接口供前端调用,两者分工明确,各司其职。
前后端分离开发的优点包括:
1、提高开发效率,前后端可以并行开发,减少了开发时间。
2、提高可维护性,前后端分离后,代码耦合度降低,修改一个模块不会影响到其他模块。
3、提高可扩展性,前后端分离后,可以更容易地添加新的功能模块。
4、提高用户体验,前端可以更快地响应用户操作,提高用户体验。

Tips:下面这张图来自网络,感觉挺贴切的。

image.png

编后语:从经典的JSP+Servlet+JavaBean的MVC时代,到SSM(Spring + SpringMVC + Mybatis)和SSH(Spring + Struts + Hibernate)的Java 框架时代,再到前端框架(KnockoutJS、AngularJS、vueJS、ReactJS)为主的MV*时代,然后是Nodejs引领的全栈时代,技术和架构一直都在进步。虽然“基于NodeJS的全栈式开发”模式很让人兴奋,但是把基于Node的全栈开发变成一个稳定,让大家都能接受的东西还有很多路要走。创新之路不会止步,无论是前后端分离模式还是其他模式,都是为了更方便得解决需求,但它们都只是一个“中转站”。走过的“中转站”可能越来越多,但是不要渐行渐远才是。


SpringBoot 和 Vue.js

Tips:前两篇文章选用 SpringBoot 作为后端框架,Vue.js 作为前端框架,也简单介绍一下。

Vue.js 和Spring Boot 是两个非常流行的开发框架,它们分别用于前端和后端开发。
简单开发流程:

  • 在 SpringBoot 中创建一个 RESTful API,使用 @RestController 注解来标记这个API;
  • 在 SpringBoot 的各层代码上,编写业务逻辑,例如利用 MyBatis 查询数据库信息;
  • 在 Vue 中使用 Axios 等库发送 HTTP 请求,与后端完成数据交互;
  • 在 Vue 组件中将返回的数据渲染到页面上;

Vue.js 的优势:

  • 响应式数据绑定:Vue.js 的核心是响应式数据绑定,它可以自动追踪数据的变化,并且更新视图。这使得开发者可以更加专注于业务逻辑的实现,而不用过多关注 DOM 操作。
  • 组件化开发:Vue.js 支持组件化开发,可以将一个页面拆分成多个组件,每个组件都有自己的状态和行为。这样可以提高代码的复用性和可维护性。
  • 轻量级:Vue.js 的体积非常小,只有 20KB 左右,加载速度非常快,可以提高用户体验。

Spring Boot 的优势:

  • 快速开发:Spring Boot 提供了自动配置和快速启动的特性,可以快速搭建一个基于 Spring 的应用程序。
  • 高度集成:Spring Boot 集成了大量的第三方库和框架,可以方便地与其他技术进行集成,如数据库、消息队列、缓存等。
  • 易于部署:Spring Boot 应用程序可以打包成一个可执行的 JAR 文件,可以方便地部署到任何支持 Java 的环境中。

综上所述,Vue.js 和 Spring Boot 都有各自的优势,它们可以很好地配合使用,实现高效的全栈开发。Vue.js 负责前端 UI 的实现和交互逻辑,Spring Boot 负责后端业务逻辑的实现和数据处理。


各层功能完善

Tips:不一定按下面代码进行封装,开发本就是天马行空,可以按自己想要的方式进行,但如果还没有相关经验,可以参考实现,并加以衍生。

Mapper 层

思路:
1、封装一个 BaseMapper 接口,将常用的增删改查方法维护好,支持泛型传递业务实体;
2、具体业务 Mapper 接口只需要实现该接口即可。

BaseMapper 接口示例:

public interface BaseMapper<T> {

    /**
     * 查询
     *
     * @param id id
     * @return 单条记录
     */
    T getById(String id);

    /**
     * 查询
     *
     * @param entity 实体
     * @return 单条记录
     */
    T get(T entity);

    /**
     * 查询列表
     *
     * @param entity 实体
     * @return 列表
     */
    List<T> findList(T entity);

    /**
     * 查询列表
     *
     * @param id id
     * @return 列表
     */
    List<T> findListById(String id) throws Exception;

    /**
     * 查询所有数据列表
     *
     * @param entity 实体
     * @return 列表
     */
    List<T> findAllList(T entity) throws Exception;

    /**
     * 插入
     *
     * @param entity 实体
     * @return 返回插入记录数
     */
    int insert(T entity);

    /**
     * 批量插入
     *
     * @param entityList 实体集合
     * @return 返回插入记录数
     */
    int insertBatch(List<T> entityList) throws Exception;

    /**
     * 更新
     *
     * @param entity 实体
     * @return 返回更新记录数
     */
    int update(T entity);

    /**
     * 批量更新
     *
     * @param entityList 实体集合
     * @return 返回更新记录数
     */
    int updateBatch(List<T> entityList) throws Exception;

    /**
     * 批量插入
     * jdbc方式
     *
     * @param entityList 实体集合
     * @return 返回插入记录数
     */
    int insertBatchByJdbc(List<T> entityList) throws Exception;

    /**
     * 批量更新
     * jdbc方式
     *
     * @param entityList 实体集合
     * @return 返回更新记录数
     */
    int updateBatchByJdbc(List<T> entityList) throws Exception;

    /**
     * 物理删除
     *
     * @param entity 实体
     * @return 返回删除记录数
     */
    int delete(T entity);

    /**
     * 批量物理删除(集合)
     *
     * @param list 集合
     * @return 返回删除记录数
     */
    @SuppressWarnings("rawtypes")
    int deleteBatchByList(List list) throws Exception;

    /**
     * 批量物理删除(数组)
     *
     * @param ids 数组
     * @return 返回删除记录数
     */
    int deleteBatchByString(String[] ids) throws Exception;

    /**
     * 更新有效标志(逻辑删除)
     *
     * @param entity 实体 必须传入 validFlag
     * @return 返回更新记录数
     */
    int updateValidFlag(T entity);
}

对应业务Mapper改造:
这里除了继承 BaseMapper,还新增了一个分页方法,基本后台管理界面都是需要查询分页逻辑的。
这里是提供 Service调用。

@Mapper
public interface ZyTeacherInfoMapper extends BaseMapper<ZyTeacherInfo> {

    /**
     * 分页获取教师信息表列表
     *
     * @param query 搜索关键词
     * @param zyTeacherInfo 查询实体
     * @return 用户列表
     */
    List<ZyTeacherInfo> findListPage(@Param("query") String query, @Param("model") ZyTeacherInfo zyTeacherInfo);
}

Service 层

思路:
1、封装一个 BaseService 类,暂不需要特殊封装;
2、封装一个 CrudService 类,代表增删改查的操作,同时继承 BaseService,同样支持泛型传递;
3、具体业务 Service 类,如果需要增删改查业务,则继承 CrudService,否则继承BaseService 。

Service 基类 BaseService:

public class BaseService {
	
}

增删改查基类 CrudService:

@Transactional(rollbackFor = Exception.class)
public class CrudService<T, D extends BaseMapper<T>> extends BaseService {

    @Autowired
    protected D dao;

    /**
     * 查询
     *
     * @param id id
     * @return 单条记录
     */
    public T getById(String id) {
        return dao.getById(id);
    }

    /**
     * 查询
     *
     * @param entity 实体
     * @return 单条记录
     */
    public T get(T entity) {
        return dao.get(entity);
    }

    /**
     * 查询列表
     *
     * @param entity 实体
     * @return 列表
     */
    public List<T> findList(T entity) {
        return dao.findList(entity);
    }

    /**
     * 查询列表
     *
     * @param id 实体
     * @return 列表
     */
    public List<T> findListById(String id) throws Exception {
        return dao.findListById(id);
    }

    /**
     * 查询所有数据列表
     *
     * @param entity 实体
     * @return 列表
     */
    public List<T> findAllList(T entity) throws Exception {
        return dao.findAllList(entity);
    }

    /**
     * 插入
     *
     * @param entity 实体
     * @return 返回插入记录数
     */
    public int insert(T entity) {
        return dao.insert(entity);
    }

    /**
     * 批量插入
     *
     * @param entityList 实体集合
     * @return 返回插入记录数
     */
    public int insertBatch(List<T> entityList) throws Exception {
        return dao.insertBatch(entityList);
    }

    /**
     * 更新
     *
     * @param entity 实体
     * @return 返回更新记录数
     */
    public int update(T entity) {
        return dao.update(entity);
    }

    /**
     * 批量更新
     *
     * @param entityList 实体集合
     * @return 返回更新记录数
     */
    public int updateBatch(List<T> entityList) throws Exception {
        return dao.updateBatch(entityList);
    }

    /**
     * 批量插入
     * jdbc方式
     *
     * @param entityList 实体集合
     * @return 返回插入记录数
     */
    public int insertBatchByJdbc(List<T> entityList) throws Exception {
        return dao.insertBatchByJdbc(entityList);
    }

    /**
     * 批量更新
     * jdbc方式
     *
     * @param entityList 实体集合
     * @return 返回更新记录数
     */
    public int updateBatchByJdbc(List<T> entityList) throws Exception {
        return dao.updateBatchByJdbc(entityList);
    }


    /**
     * 物理删除
     *
     * @param entity 实体
     * @return 返回删除记录数
     */
    public int delete(T entity) {
        return dao.delete(entity);
    }

    /**
     * 批量物理删除(集合)
     *
     * @param list 集合
     * @return 返回删除记录数
     */
    @SuppressWarnings("rawtypes")
    public int deleteBatchByList(List list) throws Exception {
        return dao.deleteBatchByList(list);
    }

    /**
     * 批量物理删除(数组)
     *
     * @param ids 数组
     * @return 返回删除记录数
     */
    public int deleteBatchByString(String[] ids) throws Exception {
        return dao.deleteBatchByString(ids);
    }

    /**
     * 更新有效标志(逻辑删除)
     *
     * @param entity 实体 必须传入 validFlag
     * @return 返回更新记录数
     */
    public int updateValidFlag(T entity) {
        return dao.updateValidFlag(entity);
    }
}

对应业务 Service 改造:

@Slf4j
@Service
public class ZyTeacherInfoService extends CrudService<ZyTeacherInfo, ZyTeacherInfoMapper> {

    /**
     * 获取用户分页列表
     *
     * @param query    搜索关键词
     * @param pageInfo 分页实体
     * @param zyTeacherInfo 实体入参
     * @return 用户列表
     */
    public PageInfo<ZyTeacherInfo> findListPage(String query, PageInfo pageInfo, ZyTeacherInfo zyTeacherInfo) {
        PageHelper.startPage(pageInfo);
        List<ZyTeacherInfo> zyTeacherInfolist = this.dao.findListPage(query, zyTeacherInfo);
        return new PageInfo<>(zyTeacherInfolist);
    }
}

这里除了继承 CrudService,还新增了一个分页接口,基本后台管理界面都是需要查询分页逻辑的。
需要引如下依赖即可:

<!-- 整合MyBatis分页插件 -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.2.5</version>
</dependency>

Controller 层

思路:
1、封装一个 BaseController 基类,将共用的控制层方法维护好;
2、具体业务 Controller 类只需要继承该类即可。

BaseController 接口示例:

public class BaseController {

    /**
     * 添加自定义绑定
     * @param binder 绑定
     */
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.addCustomFormatter(new LongToDateFormatter());
    }
}

这里通过 @InitBinder 注解实现了 Long 自动转 Date 的参数转换功能,这个是非必须的,只是给一个例子,基类可以做这类事情。

对应业务 Controller 改造:

@RestController
@Api(value = "ZyTeacherInfoController", tags = {"教师信息表服务"})
@RequestMapping(value = "/zyTeacherInfo")
public class ZyTeacherInfoController extends BaseController {

    /**
     * 教师信息表服务
     */
    @Autowired
    private ZyTeacherInfoService zyTeacherInfoService;

    @ApiOperation(value = "获取教师信息表列表")
    @GetMapping("")
    public List<ZyTeacherInfo> findList(ZyTeacherInfo zyTeacherInfo) {
        return zyTeacherInfoService.findList(zyTeacherInfo);
    }

    @ApiOperation(value = "获取教师信息表列表(分页)", response = PageInfo.class)
    @ApiImplicitParams({@ApiImplicitParam(name = "query", value = "关键词", dataType = "string"), @ApiImplicitParam(name = "pageNum", value = "当前页码", required = true, dataType = "int"),})
    @GetMapping(value = "/page")
    public PageInfo<ZyTeacherInfo> page(String query, PageInfo pageInfo, ZyTeacherInfo zyTeacherInfo) {
        return zyTeacherInfoService.findListPage(query, pageInfo, zyTeacherInfo);
    }

    @ApiOperation(value = "获取教师信息表详细信息", notes = "根据url的id来获取教师信息表详细信息")
    @ApiImplicitParam(name = "id", value = "教师信息表ID", required = true, dataType = "String")
    @GetMapping("/{id}")
    public ZyTeacherInfo get(@PathVariable String id) {
        return zyTeacherInfoService.getById(id);
    }

    @ApiOperation(value = "创建教师信息表")
    @PostMapping("/insert")
    public void insert(ZyTeacherInfo zyTeacherInfo) {
        zyTeacherInfo.setCreatedTime(new Date());
        zyTeacherInfo.setTeaCode(IdUtil.fastSimpleUUID());
        zyTeacherInfoService.insert(zyTeacherInfo);
    }

    @ApiOperation(value = "更新教师信息表详细信息")
    @ApiImplicitParams({@ApiImplicitParam(name = "id", value = "教师信息表ID", required = true, paramType = "query", dataType = "String")})
    @PostMapping("/update")
    public void update(ZyTeacherInfo zyTeacherInfo) {
        zyTeacherInfo.setModifiedTime(new Date());
        zyTeacherInfoService.update(zyTeacherInfo);
    }

    @ApiOperation(value = "删除教师信息表", notes = "删除教师信息表")
    @ApiImplicitParam(name = "id", value = "教师信息表ID", required = true, paramType = "query", dataType = "String")
    @PostMapping("/delete")
    public void deleteZyTeacherInfo(ZyTeacherInfo zyTeacherInfo) {
        zyTeacherInfoService.delete(zyTeacherInfo);
    }
}

这里使用了 Hutool 工具类生成 uuid,需要引入如下依赖:

<!-- 整合Hutool  -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.17</version>
</dependency>

后端经验分享

上面把各层的功能都丰富起来了,也做了若干封装,后端功能初具雏形。
可以用 postman 测试一下几个接口,也可以集成 swaggerUI 进行单元测试。
接下来,准备完善一下前端了!

拦截器、过滤器、监听器

这块虽然不是需求挂钩的刚需,但是哪个领导能拒绝你为你的项目添砖加瓦呢?
这几块看着写点东西,内容这边不重复列了,可以参考下面几篇文章。
《知识点扫盲 · 拦截器 Interceptor》
《知识点扫盲 · 过滤器 Filter》
《知识点扫盲 · 监听器 Listener》
《知识点扫盲 · Redis 事件监听》


更多内容

篇幅所限,下一篇文章再继续吧,不然读者看起来也甚是乏累。


总结陈词

本系列博文更新到第三篇了,博主仍能保持初心,难能可贵。
期望可以帮助初入职场的大家,快速适应企业开发,多一些机会。
”长江后浪推前浪,一代新人换旧人“,加油,年轻人!
💗 如果觉得内容还可以,麻烦点个关注不迷路,您的鼓励是我创作的动力。

CSDN_END.gif

  • 18
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

战神刘玉栋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值