SpringBoot項目后端開發(fā)邏輯全面梳理
SpringBoot項目中包含Mapper層(Dao層)、Entity層(model層)、DTO層、VO層、Service層和Controller層(本篇以學生信息表增刪改查為例,梳理各個層之間的邏輯關系、開發(fā)流程和注意事項)。
一、各層之間的邏輯關系
1.Controller層、Service層、Mapper層、Entity層的邏輯關系
2.每層的理解
1.mapper層(Dao層)。
mapper層是操作數(shù)據(jù)庫的一層。想要訪問數(shù)據(jù)庫并且操作,只能通過mapper層向數(shù)據(jù)庫發(fā)送sql語句,將這些通過接口傳給service層,對數(shù)據(jù)庫進行操作。主要實現(xiàn)增刪改查操作,在mybatis中與xxx.xml內(nèi)相互一一映射。
mapper層包含xxxDao.java文件和xxxDao.xml
(.xml文件中寫sql語句,配置通用查詢映射結果)
(Dao.java相當于xml文件的抽象類)
StudentBaseDao.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.ai.citicnet.boss.manage.service.dao.student.StudentBaseDao"> <sql id="baseColumns"> id, name, sex, age, chinese, math, tenglish </sql> <select id="queryList" parameterType="com.ai.citicnet.boss.manage.remote.model.vo.student.studentBaseForm" resultType="com.ai.citicnet.boss.manage.remote.model.dto.student.studentDTO"> select * from student </select> <select id="queryRepeat" parameterType="java.util.Map" resultType="com.ai.citicnet.boss.manage.remote.model.dto.student.studentDTO"> select name from student where name = #{abc} <if test="id != null and id != 0"> and id != #{id} </if> </select> </mapper>
StudentBaseDao.java
package com.ai.citicnet.boss.manage.service.dao.student; import com.ai.citicnet.boss.manage.remote.model.dto.rs.RsCheckListInfo; import com.ai.citicnet.boss.manage.remote.model.dto.student.studentDTO; import com.ai.citicnet.boss.manage.remote.model.vo.student.studentBaseForm; import com.ai.citicnet.boss.manage.service.entity.Student.StudentBase; import com.ai.citicnet.boss.manage.service.entity.quote.QuoteBase; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; import org.springframework.stereotype.Repository; import java.util.List; import java.util.Map; @Mapper @Repository public interface StudentBaseDao extends BaseMapper<StudentBase> { List<studentDTO> queryList(studentBaseForm v); //studentDTO queryRepeat(Map<String,String> param); //List<studentDTO> queryRepeat(studentBaseForm v); List<studentDTO> queryRepeat(String abc,int id); }
注意:
- namespace和resultMap的type要指向正確的地址:namespace指向mapper文件,type指向?qū)嶓w類。
- parameterType用于對應的mapper接口方法接收的參數(shù)類型,將信息存入到數(shù)據(jù)庫中。
- resultType用于指定sql輸出的結果類型,從數(shù)據(jù)庫中提取相應的數(shù)據(jù)。
- .xml文件命名的id與.java文件中的名稱對應
2.Entity層(model層)
也叫作pojo層,是數(shù)據(jù)庫在項目中的類,在文件包含實體類的屬性和對應屬性的get、set方法。
實體類中屬性同數(shù)據(jù)庫表字段一一對應,對于相應的get、set方法一般不需要書寫,實體類上引入@Data注解,會自動注入get、set以及toString方法,減少代碼量。
entity表示對數(shù)據(jù)庫中所有標的映射,是根據(jù)數(shù)據(jù)庫表字段設計出來的實體(要求表名和類名相同,字段名與成員變量名相同)
StudentBase.java
package com.ai.citicnet.boss.manage.service.entity.Student; import com.ai.citicnet.boss.manage.service.entity.BaseEntity; import lombok.*; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import java.math.BigDecimal; @Data @NoArgsConstructor @AllArgsConstructor @TableName (value ="student") public class StudentBase { //private static final long serialVersionUID = 1999600885795452253L; @TableField(value = "id") @TableId private int id; @TableField(value = "name") private String name; @TableField(value = "sex") private String sex; @TableField(value = "age") private String age; @TableField(value = "chinese") private String chinese; @TableField(value = "math") private String math; @TableField(value = "english") private String english; }
3.DTO層
數(shù)據(jù)對象傳輸層,負責屏蔽后端實體類,將UI要的數(shù)據(jù)進行重新定義和封裝。
DTO里的每個字段,與前端頁面對應。
因為后端在實際業(yè)務場景中需要儲存大量數(shù)據(jù),而用戶需要的只是一部分,為了快速獲取用戶需要的數(shù)據(jù),應該把用戶經(jīng)常用到的數(shù)據(jù)在DTO層進行封裝,在調(diào)用服務時,只需要調(diào)用依次便可完成所有的邏輯操作。
邏輯關系如下:
studentDTO.java
package com.ai.citicnet.boss.manage.remote.model.dto.student; import com.ai.citicnet.boss.common.model.BaseInfo; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.List; @Data @NoArgsConstructor @AllArgsConstructor public class studentDTO { private int id; private String name; private String sex; private String age; private String chinese; private String math; private String english; private String sumGrade; // private String sGrade; }
4.VO層
視圖對象,用于展示層返回給前端。
把某個指定頁面的所有數(shù)據(jù)封裝起來,方便前端獲取數(shù)據(jù),后端將前端需要的數(shù)據(jù)做整合,打包成一個類。
使用場景:如果在前端頁面需要展示經(jīng)過某些數(shù)據(jù)庫操作才能展示的特定數(shù)據(jù),一般在vo層中把操作過程中涉及到的數(shù)據(jù)進行封裝,方便前端獲取。在controller層定義對應接口時把返回類型規(guī)定為vo類。
studentBaseForm.java
package com.ai.citicnet.boss.manage.remote.model.vo.student; import com.ai.citicnet.boss.manage.remote.utils.ManagePageRequest; import lombok.Data; @Data public class studentBaseForm extends ManagePageRequest { private int id; private String name; private int sex; private int age; private int chinese; private int math; private int english; }
5.service層
業(yè)務服務層,調(diào)用mapper層API并提供給controller層使用,間接和數(shù)據(jù)庫打交道。
包括兩部分,接口文件(Service.java)和接口實現(xiàn)類文件(ServiceImpl.java)。
接口文件中定義在controller層中調(diào)用的service層方法;接口實現(xiàn)類文件中完成service層接口中定義的方法的實現(xiàn)。
在該層進行復雜的業(yè)務邏輯處理,在對多個mapper層查到的數(shù)據(jù)進行組裝、處理,然后將結果返回給controller。因此,在一般情況下,一個controller中可能包括多個Service,而一個Service中又或許包含多個mapper。
注意:這里接口實現(xiàn)類中方法的實現(xiàn)是指業(yè)務邏輯的實現(xiàn),可能有些方法并不能在實現(xiàn)類里完成真正意義上的實現(xiàn),還需要在mapper層文件完成其真正意義上的實現(xiàn)(主要是和數(shù)據(jù)庫交互)。
StudentBaseService.java
package com.ai.citicnet.boss.manage.service.service.student; import com.ai.citicnet.boss.manage.remote.model.dto.student.studentDTO; import com.ai.citicnet.boss.manage.remote.model.vo.student.studentBaseForm; import com.ai.citicnet.boss.manage.remote.utils.ManagePageResult; import com.ai.citicnet.boss.manage.service.entity.Student.StudentBase; import com.baomidou.mybatisplus.extension.service.IService; import java.util.List; public interface StudentBaseService extends IService<StudentBase> { ManagePageResult studentList(studentBaseForm vo); studentDTO studentDetail(int name); void saveStudent(studentDTO studentDTO) throws Exception; void delUser(int name); }
StudentBaseServiceImpl.java
package com.ai.citicnet.boss.manage.service.service.impl.student; import com.ai.citicnet.boss.manage.remote.model.dto.student.studentDTO; import com.ai.citicnet.boss.manage.remote.model.vo.student.studentBaseForm; import com.ai.citicnet.boss.manage.remote.utils.ManagePageResult; import com.ai.citicnet.boss.manage.service.dao.student.StudentBaseDao; import com.ai.citicnet.boss.manage.service.entity.Student.StudentBase; import com.ai.citicnet.boss.manage.service.exception.ManageException; import com.ai.citicnet.boss.manage.service.service.UserInfoService; import com.ai.citicnet.boss.manage.service.service.student.StudentBaseService; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; @Slf4j @Service("studentBaseService") public class StudentBaseServiceImpl extends ServiceImpl<StudentBaseDao, StudentBase> implements StudentBaseService { @Autowired StudentBaseService studentBaseService; @Autowired UserInfoService userInfoService; @Override public ManagePageResult studentList(studentBaseForm vo) { PageHelper.startPage(vo.getPageNum(), vo.getPageSize()); List<studentDTO> pageList = this.baseMapper.queryList(vo); for(studentDTO item : pageList){ int chinese = Integer.parseInt(item.getChinese()); int math = Integer.parseInt(item.getMath()); int english = Integer.parseInt(item.getEnglish()); int sum = chinese+math+english; if((sum)>180){ item.setSumGrade("及格"); }else{ item.setSumGrade("不及格"); } } PageInfo<studentDTO> pageInfo = new PageInfo<>(pageList); return ManagePageResult.getPageResult(pageInfo); } @Override public studentDTO studentDetail(int studentNo) { if (ObjectUtils.isEmpty(studentNo)) { throw new ManageException("error!"); } QueryWrapper<StudentBase> wrapper = new QueryWrapper<>(); wrapper.eq("id", studentNo); StudentBase studentBase = this.baseMapper.selectOne(wrapper); if (ObjectUtils.isEmpty(studentBase)) { throw new ManageException("未找到對應的信息,請檢查輸入是否正確!"); } studentDTO studentDTO = new studentDTO(); BeanUtils.copyProperties(studentBase, studentDTO); return studentDTO; } @Override @Transactional(rollbackFor = Exception.class) public void saveStudent(studentDTO studentDTO) throws Exception { //保存信息 StudentBase studentBase = new StudentBase(); BeanUtils.copyProperties(studentDTO, studentBase); List<studentDTO> result = this.baseMapper.queryRepeat(studentBase.getName(),studentBase.getId()); if(ObjectUtils.isEmpty(result)){ this.save(studentBase); }else{ throw new ManageException("用戶名重復"); } } //刪除 @Override @Transactional(rollbackFor = Exception.class) public void delUser(int name) { log.info("刪除信息入?yún)ⅲ骸緖}】", name); if (ObjectUtils.isEmpty(name)) { throw new ManageException("入?yún)㈠e誤!"); } // QueryWrapper<StudentBase> wrapper = new QueryWrapper<>(); // wrapper.eq("id", name); // StudentBase studentBase = this.baseMapper.selectOne(wrapper); this.removeById(name); } }
6.Controller層
本層定義接口并調(diào)用service邏輯設計層的接口來控制業(yè)務流程。
功能:接收前端請求,調(diào)用service,接收service返回的數(shù)據(jù),再將處理結果返回到前端(接收前端數(shù)據(jù),返回頁面請求信息)。
Controller層是不允許直接操作數(shù)據(jù)庫的!它就像一個服務員,有客人需要點菜,就喊服務員;對前端需要完成什么業(yè)務,就告訴Controller。Controller只是一個中間者或轉(zhuǎn)發(fā)者。
不能在Controller里暴露Service的業(yè)務邏輯,而應該直接轉(zhuǎn)發(fā)Service的業(yè)務處理結果。
QuoteBaseController11.java
package com.ai.citicnet.boss.manage.service.controller.quote; import com.ai.citicnet.boss.manage.remote.model.dto.student.studentDTO; import com.ai.citicnet.boss.manage.remote.model.vo.student.studentBaseForm; import com.ai.citicnet.boss.manage.remote.utils.ManagePageResult; import com.ai.citicnet.boss.manage.remote.utils.ResponseResult; import com.ai.citicnet.boss.manage.service.enumeration.common.RespStatus; import com.ai.citicnet.boss.manage.service.service.student.StudentBaseService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/studentBase") public class QuoteBaseController11 { @Autowired // spring自動注入studentBaseService賦值 private StudentBaseService studentBaseService; //PostMapping用于認證方法,將http post請求映射到特定處理程序 @PostMapping("/studentList") public ResponseResult queryList(@RequestBody studentBaseForm vo) { try { ManagePageResult page = studentBaseService.studentList(vo); return ResponseResult.ok().put(page); } catch (Exception e) { e.printStackTrace(); return ResponseResult.error(RespStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage()); } } @PostMapping("/studentDetail") public ResponseResult studentDetail(@RequestParam("name") int studentNo) { try { studentDTO studentDTO = studentBaseService.studentDetail(studentNo); return ResponseResult.ok().put(studentDTO); } catch (Exception e) { e.printStackTrace(); return ResponseResult.error(RespStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage()); } } //保存數(shù)據(jù) @PostMapping("/saveStudent") public ResponseResult saveStudent(@RequestBody studentDTO studentDTO) { try { studentBaseService.saveStudent(studentDTO); return ResponseResult.ok().put(true); } catch (Exception e) { e.printStackTrace(); return ResponseResult.error(RespStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage()); } } //刪除數(shù)據(jù) @PostMapping("/deleteStudent") public ResponseResult deleteStudent(@RequestParam("name") int name) { try { studentBaseService.delUser(name); return ResponseResult.ok().put(true); } catch (Exception e) { e.printStackTrace(); return ResponseResult.error(RespStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage()); } } }
二、運行流程
- 1.控制層接收前端請求,調(diào)用對應的業(yè)務層接口方法
- 2.業(yè)務層實現(xiàn)類去實現(xiàn)業(yè)務層接口
- 3.業(yè)務層實現(xiàn)類的方法調(diào)用數(shù)據(jù)層的接口
- 4.數(shù)據(jù)層實現(xiàn)文件(mapper.xml)實現(xiàn)數(shù)據(jù)層接口
- 5.處理結果,逐層返回。
三、開發(fā)流程
- 1.實體類
- 2.service接口
- 3.XML文件
- 4.mapper接口
- 5.serviceImpl實現(xiàn)類
- 6.controller層調(diào)用接口
- 7.最終接口展示
1、實體類
實體類連接著前端接口內(nèi)容,返回的響應參數(shù)、參數(shù)的數(shù)據(jù)類型和注釋都來自于這里;所以我們把創(chuàng)建實體類作為開始編碼的第一步。
建立實體類,就能知道我們最終是想要得到什么值返回給前端,這樣,我們以后的步驟都是圍繞著如何得到我們的實體類而來。
注意:
因為很多時候返回給前端的是一個集合類型,存儲著很多的數(shù)據(jù)。這個時候,我們一般建立兩個實體類,一個用來存儲List<>集合,另一個用來存儲該集合里的內(nèi)部數(shù)據(jù)。
package com.hncr.system.domain.vo; //用戶集合實體類 @Data public class UserVo { @ApiModelProperty("用戶集合") private List<UserListVo> userListVos; }
package com.hncr.system.domain.vo; //用戶類 @Data public class UserListVo { @ApiModelProperty("姓名") private String name; @ApiModelProperty("年齡") private Integer age; @ApiModelProperty("性別:(0:男,1:女)") private Integer sex; }
2、Service接口
“接口編程”思想大概就是基于Service接口進行的吧!
返回值類型是UserVo實體類(因為最終要得到的值就是要返回一個集合給前端,所以這里的返回類型就是該實體類)
package com.hncr.system.service; import com.hncr.system.domain.vo.UserVo; //Service接口 public interface IUserService { UserVo userSummary(); }
3、xml文件
接下來,我們將sql語句寫好(很重要?。。。?/p>
確定需要多少個sql語句才能解決具體的Service接口問題。
可以先在Navicate中做一些實力操作(根據(jù)年齡進行排序)
SELECT name, age, sex FROM `user` ORDER BY age DESC
(在實際項目中,要查詢或者改變的操作會很多,數(shù)據(jù)量也會很大,很多時候要對某個字段做索引操作才會縮短查詢時間,查詢時間超過3秒或4秒的sql,在前端來看就是一個相當慢的接口了。)
數(shù)據(jù)量大的數(shù)據(jù)庫,我們盡量做到只訪問一次,以免浪費不必要的時間,影響用戶體驗。
在Navicat中查到想要的數(shù)據(jù)后,把sql語句加入到我們的XML文件中,結合我們的namespace、id、resultType(第四步會配置)。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.hncr.system.mapper.UserMapper"> <select id="userSummaryMapper" resultType="java.util.Map"> SELECT name, age, sex FROM `user` ORDER BY age DESC </select> </mapper>
4、Mapper接口
第三步確定,一條sql語句就可以搞定想要的效果,那Mapper接口寫一個即可(反正就是sql寫了幾個,我們對應的就得寫幾個Mapper接口)。
package com.hncr.system.mapper; import org.springframework.stereotype.Repository; import java.util.List; import java.util.Map; //Mapper接口 @Repository public interface UserMapper { List<Map> userSummaryMapper(); }
可以看到,這里的返回值類型不太一樣了,我們要確保xml文件中的resultType要和Mapper接口中的一一對應上,否則查詢不到數(shù)據(jù),接口報錯。
(在實際項目中,后續(xù)的這種接口方式用的較多,list、Map、List<Map>)
集合很重要,項目中大多數(shù)都會使用集合?。?!
id對應上我們的Mapper接口名字,這里是“userSUmmaryMapper”
注意:
- xml文件多少個sql,mapper接口就寫多少個。
- mapper接口返回類型,接口名稱要與xml文件中resultType、id 等屬性一一對上。
5、ServiceImpl實現(xiàn)類
至關重要的一步?。。?/strong>
在這里要實現(xiàn)數(shù)據(jù)的交互、代碼邏輯。
package com.hncr.system.service.impl; import com.hncr.system.domain.vo.UserListVo; import com.hncr.system.domain.vo.UserVo; import com.hncr.system.mapper.UserMapper; import com.hncr.system.service.IUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; import java.util.Map; @Service public class UserServiceImpl implements IUserService { @Autowired private UserMapper userMapper; List<Map> userSummaryMapper(){ return userMapper.userSummaryMapper(); } public UserVo userSummary(){ UserVo userVo = new UserVo(); List<Map> list1 = userSummaryMapper(); List<UserListVo> list2 = new ArrayList<>(); for (int i = 0; i < list1.size(); i++) { UserListVo userListVo = new UserListVo(); Map map = list1.get(i); userListVo.setName((String) map.get("name")); userListVo.setAge((Integer) map.get("age")); userListVo.setSex((Integer) map.get("sex")); list2.add(userListVo); } userVo.setUserListVos(list2); return userVo; } }
6、Controller層調(diào)用接口
走到這里,基本就迎來了收尾工作。
Controller層就是一個調(diào)用的過程,將相關的注解寫好,注入相關的接口方法,然后返回給前端。這樣我們就完成了其中一個接口。同理再逐個完成其他接口就可以啦。
package com.hncr.web.controller.system; import com.hncr.system.domain.JsonResult; import com.hncr.system.domain.vo.UserVo; import com.hncr.system.service.IUserService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @Api(value = "用戶信息", tags = "用戶信息") @RequestMapping("/user") public class UserController { @Autowired private IUserService userService; @ApiOperation(value = "用戶信息匯總", notes = "{<br/>" + " \"msg\": \"操作成功\",<br/>" + " \"code\": 200,<br/>" + " \"data\": {<br/>" + " }<br/>" + "}") @GetMapping("UserSummary") public JsonResult<UserVo> userSummary(){ UserVo userVo = userService.userSummary(); return JsonResult.success(userVo); } }
一個完整的SpringBoot項目的完整后端開發(fā)就這樣完成啦?。?!
總結
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Spring Boot jar 啟動時設置環(huán)境參數(shù)的操作
這篇文章主要介紹了Spring Boot jar 啟動時設置環(huán)境參數(shù)的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06SpringBoot如何獲取application.properties中自定義的值
這篇文章主要介紹了SpringBoot獲取application.properties中的自定義的值,目錄結構文件代碼給大家列舉的非常詳細,需要的朋友可以參考下2021-09-09springboot使用redis緩存亂碼(key或者value亂碼)的解決
在通過springboot緩存數(shù)據(jù)的時候,發(fā)現(xiàn)key是一堆很不友好的東西,本文主要介紹了springboot使用redis緩存亂碼(key或者value亂碼)的解決,感興趣的可以了解一下2023-11-11基于java+springboot+mybatis+laiyu實現(xiàn)學科競賽管理系統(tǒng)
這篇文章主要介紹了基于java+springboot+mybatis+laiyu實現(xiàn)的學科競賽管理系統(tǒng),本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-09-09SpringMVC 攔截器不攔截靜態(tài)資源的三種處理方式方法
本篇文章主要介紹了SpringMVC 攔截器不攔截靜態(tài)資源的三種處理方式方法,詳細的介紹了三種方法,有興趣的可以了解一下。2017-01-01