数据库、后端(主要控制层注解的使用场景)
数据库
- 一个系列功能的表名尽量统一规范,如:goods、goods_dictionary、goods_xxx,就要改 goods 为 goods_info 统一
- 表名和字段名都是各自统一大小写(数据库不区分大小写),所以一般就表名全小写,字段名全大写,字母之间下划线衔接
- 数据表 ID 不是自增长、不是 int,而是 varchar 类型的后端自行生成的,如:
goods.setGoodsId(IDUtils.getUUID32());
UUID 自动生成 32 位的 ID,且不会显现到公开页面上 - 字典表 dictionary 主要字段 CODE、PCODE、NAME,PCODE 可为空,空就表示是没有父编码的最顶层了
- 涉及到级联部分会在后面再说明
后端
entity、mapper、dao、service
先前所学
- entity
- 对应数据库的每一个表,内写对应的所有字段的属性名,alt + insert 快捷加 Getter and Setter、toString()
- mapper.xml 文件位置方式
- 都放在 resources 下严格与 dao 同目录下的地方
- resources 下不严格要求,但要配置文件配置:
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
- dao 层编写接口,mapper.xml 编写 sql 语句实现接口方法
- mapper.xml 里
<mapper namespace="com.qst.项目名.dao.dao接口">
绑定 - dao 层方法名的参数列表中加 @Param:不同参数名需要明确参数名时转换
@Param("ANO") no
,或是在多个参数时使用(单个的话可以自行解析) - mapper 里
数据库列名 = #{传来的参数}
- mapper.xml 里
- service 层里同样接口,impl 继承
- @Service 绑定在 impl 中,@Autowired 自动注入 dao 层的接口,impl 若无需要的额外操作就直接 return dao 的方法名即可
- 小提一嘴:不用 mapper.xml 方式时,就是给 dao 的 impl 加上 @Repository 注解
实用方式
-
最重要的一点:千万不要按照自己的原来编写的风格和目录样式来
-
entity
- 推荐使用 @Data —— lombok 依赖自动生成
toString
、equals
、hashCode
、getter
和setter
等方法 - 实体类里可以划分出多个文件夹存放不同情况下用的实体类,如 PO、DTO、VO、BO 等,根据统一需求来,一般情况下只用 entity 也无妨
- 推荐使用 @Data —— lombok 依赖自动生成
-
mapper.xml
-
同是在 resources 下,位置按框架配置要求来,大体样式并无不同
-
<mapper namespace="自行定义为文件也可,由于可读性也会要求写java下的mapper全路径">
(爆红也无碍),对应的,在使用时private static final String NAMESPACE = "与mapper里的namespace同值";
匹配(下面 service 的 impl 会说) -
<select>
同一个表的话就全写在一起,不要有一个表但存在多个查询,不同的要求全部用动态 sql 语句编辑,如:<select id="selectGoods" parameterType="cn.com.xxx.xxx.entity.Goods" resultType="cn.com.xxx.xxx.entity.Goods"> select g.GOODS_ID, g.GOODS_NAME, g.GOODS_CODE, g.GOODS_DATE, g.GOODS_STATE, g.GOODS_NUM_STOCK, g.GOODS_PRICE, g.GOODS_BRAND, g.GOODS_TEL, gd.GOODS_DICTIONARY_NAME, gd.GOODS_DICTIONARY_CODE, gd.GOODS_DICTIONARY_PCODE, gd.GOODS_DICTIONARY_TYPE, gt.TREE_CODE, gt.TREE_PARENT_CODE, gt.TREE_NAME from goods_info g left join goods_dictionary gd on g.GOODS_CODE = gd.GOODS_DICTIONARY_CODE left join goods_tree gt on g.GOODS_TREE_CODE = gt.TREE_CODE <where> <if test="goodsName!=null and goodsName != ''"> AND g.GOODS_NAME like CONCAT('%',#{goodsName},'%') </if> <if test="goodsState!=null and goodsState != ''"> AND g.GOODS_STATE = #{goodsState} </if> <if test="goodsCode!=null and goodsCode != ''"> AND g.GOODS_CODE = #{goodsCode} </if> <if test="treeCodeList!= null and treeCodeList.size() > 0"> AND g.GOODS_TREE_CODE in <foreach collection="treeCodeList" open="(" close=")" separator="," item="treeCode"> #{treeCode} </foreach> </if> <if test="goodsPcodeList!= null and goodsPcodeList.size() > 0"> AND g.GOODS_CODE in <foreach collection="goodsPcodeList" open="(" close=")" separator="," item="childCode"> #{childCode} </foreach> </if> </where> ORDER BY GOODS_NAME </select>
-
一般都是配置好了驼峰转换,若是没有转换需要用 resultMap:
<select id="selectGoods" parameterType="cn.com.xxx.xxx.entity.Goods" resultMap="goodsResultMap"> <!-- ...... --> <resultMap id="goodsResultMap" type="cn.com.xxx.xxx.entity.Goods"> <id property="goodsId" column="GOODS_ID"/> <result property="goodsName" column="GOODS_NAME"/> <result property="goodsCode" column="GOODS_CODE" /> <result property="goodsDate" column="GOODS_DATE" javaType="java.time.LocalDateTime" jdbcType="TIMESTAMP"/> <result property="goodsState" column="GOODS_STATE"/> <result property="goodsNumStock" column="GOODS_NUM_STOCK"/> <result property="goodsPrice" column="GOODS_PRICE"/> <result property="goodsBrand" column="GOODS_BRAND"/> <result property="goodsTel" column="GOODS_TEL"/> <association property="goodsDic" javaType="cn.com.victorysoft.vs.entity.GoodsDic"> <result property="goodsDicId" column="GOODS_DICTIONARY_ID"/> <result property="goodsDicName" column="GOODS_DICTIONARY_NAME"/> <result property="goodsDicCode" column="GOODS_DICTIONARY_CODE"/> <result property="goodsDicPcode" column="GOODS_DICTIONARY_PCODE"/> <result property="goodsDicType" column="GOODS_DICTIONARY_TYPE"/> </association> <association property="goodsTree" javaType="cn.com.victorysoft.vs.entity.GoodsTree"> <result property="treeId" column="TREE_ID"/> <result property="treeCode" column="TREE_CODE"/> <result property="treeParentCode" column="TREE_PARENT_CODE"/> <result property="treeName" column="TREE_NAME"/> </association> </resultMap>
-
-
更新也不要全写进去,而是做判断:
<update id="updateGoodsById" parameterType="cn.com.xxx.xxx.entity.Goods"> update goods_info <set> <if test="goodsName!=null"> GOODS_NAME = #{goodsName} </if> <if test="goodsCode!=null"> , GOODS_CODE = #{goodsCode} </if> </set> where GOODS_ID = #{goodsId} </update>
-
批量删除同理:
<delete id="deleteSomeGoodsByIds" parameterType="list" > delete from goods_info where GOODS_ID in <foreach collection="list" open="(" close=")" separator="," item="id"> #{id} </foreach> </delete>
-
- mapper 里不要写 ' * ',若是获取全部就全部都写上
- 查询的标签上要写上
parameterType
指定输入参数的类型,resultType
是期望 MyBatis 将查询结果映射到的 Java 类型,简单来说就是指定查询返回的样式- 若是使用了
resultMap
来进行更复杂的结果映射,就可以省略resultType
- 若是使用了
-
-
dao、service
- 在提供了封装了 MyBatis 常用语句的类下,可以不加 dao 层接口或 impl,也不需要 service 的接口,直接在 impl 里加上
private static final String NAMESPACE = "与mapper里的namespace同值";
,这样就可以匹配到 mapper 的方法,使用时:封装的方法参数就是(statement, parameter)
:(NAMESPACE + ".mapper的方法id", 传参值);
- 在提供了封装了 MyBatis 常用语句的类下,可以不加 dao 层接口或 impl,也不需要 service 的接口,直接在 impl 里加上
conroller
大体回顾
-
@RestController
标识一个类是 RESTful 风格的控制器,大体样式:@Api(value = "xxxxxx", tags = "xxxxxx") @RestController @RequestMapping("/xxx") public class XxxController { @Autowired private XxxService xxxService; /** *尽量标注每个方法的作用 * @Param 必要时备注参数含义 * @return */ @ApiOperation(value = "xxxx", notes = "xxxx") @PostMapping("yyyXxx") public 封装好的类型 yyyXxx(@ApiParam(name = "page", value = "页面", required = true) @RequestParam (value = "page", required = true, defaultValue = "1") int page, @ApiParam(name = "rows", value = "行数", required = true) @RequestParam (value = "rows", required = true, defaultValue = "#{T(java.lang.Integer).MAX_VALUE}") int rows, @ApiParam(name = "xxx", value = "xxxx", required = true) @RequestBody Xxx xxx) { JsonMessage success = new JsonMessage().success(xxxService.yyyyXxxx(xxx, page, rows)); return success; }
@RestController
:是@Controller
和@ResponseBody
的组合,表示该类的所有方法都会返回数据而不是视图@GetMapping
:映射 GET 请求@PostMapping
:映射 POST 请求@PutMapping
:映射 PUT 请求@DeleteMapping
:映射 DELETE 请求
@Api
、@ApiOperation
、@ApiParam
等: Swagger 注解用于生成 API 文档,提供给开发者或前端人员查看和理解 API 的用途和参数,此处参考规范要求- “ 封装好的类型 ”:就是在前后端分离情况下需要规定返回样式供前端使用,所需内容也都包含在其中,每次发送到前端都是此种样式,也就例如方法最后返回的
JsonMessage
" #{T(java.lang.Integer).MAX_VALUE} "
:是使用 SpEL(Spring Expression Language,Spring 表达式语言)表达式来指定默认值的一种方式,含义是获取 Java 中Integer
类的MAX_VALUE
静态字段的值,即整数的最大值(在 Spring 表达式中,T() 是一个类型运算符,用于获取类或接口的静态字段或方法)- 根据需求 defaultValue 也可以直接写一个数值,例如 " 10 "
各种注解作用
-
上述后端代码对应的前端样式就是:
const url = xxx + "/yyyXxx"; axiosUtil.post(`${url}?page=${currentPage}&rows=${pageSize}`, { xxxDic: { xxxDicType: xxxDicType, xxxDicPcode: xxxPcode, }, xxxName: goodsName } // ...... )
-
@RequestParam
是从请求中提取查询参数,可以用于方法的参数上,将请求中的参数值映射到方法的参数上- 也就是上述的
page=${currentPage}&rows=${pageSize}
部分
- 也就是上述的
-
@RequestBody
用于从请求体中提取数据,通常用于接收客户端发送的 JSON 或 XML 格式的数据- 前端使用 Axios 等库发送 HTTP 请求时,编写的看到的发送的一般都是 JavaScript 对象字面量样式,然后发送时会自动将 JavaScript 对象转换为 JSON 格式由后端的
@RequestBody
解析获取 - 具体发送代码会在后面前端 vue 处呈现
- 前端使用 Axios 等库发送 HTTP 请求时,编写的看到的发送的一般都是 JavaScript 对象字面量样式,然后发送时会自动将 JavaScript 对象转换为 JSON 格式由后端的
-
这里就不得不提一些概念了:
-
Java 对象:就是常见的后端编码的对象,常见的就是些实体类的对象,后端写的几乎都是 Java 对象的样式,如
public class Goods { String goodsName; String goodsState; GoodsDic goodsDic; String treeCode; // ...... Setter、Getter...... }
-
JavaScript 对象:前端常编写的样式,如
{ goodsName: goodsName, goodsState: goodsState, goodsDic: { goodsDicType: goodsDicType, goodsDicPcode: goodsPcode, }, treeCode: treeCode, }
- 这里的冒号后面的都是数据值
-
JSON 对象:就是 JavaScript 对象键值都加上双引号,如
{ "goodsName": "goodsNameValue", "goodsState": "goodsStateValue", "goodsDic": { "goodsDicType": "goodsDicTypeValue", "goodsDicPcode": "goodsPcodeValue" }, "treeCode": "treeCodeValue" }
- 这里的冒号后面的都是数据值
-
-
@PathVariable
用于处理路径变量的注解,它主要用于从请求路径中提取变量值,例如参数列表为("yyyXxx/{xxxId}")
样式时,就能用@PathVariable("xxxId") String xxxId
从 url 中获取 -
注意,
@PathVariable
和@RequestParam
不一样-
@PathVariable
用于提取 URI 模板中的变量,通常用于提取路径中的变量,例如,/example/{id}
中的{id}
就是一个路径变量,前端就是const url = 'xxx/xxx/xxx' axiosUtil.post(`${url}/${goodsId}`, ......
-
@RequestParam
用于提取查询参数,即在 URL 中通过?key=value
形式传递的参数,前端就是const url = 'xxx/xxx/xxx' axiosUtil.post(`${url}?page=${currentPage}&rows=${pageSize}`, ......
-
只有
@RequestBody
是用于 POST,其他两种是都适用,前端请求是 GET,并且参数作为 URL 的一部分,那么直接使用@RequestParam
,一般不写的话 Spring MVC 默认会将这些参数作为@RequestParam
处理
-
-
举个例子,批量删除时需要后端获取 id 数组, 如:["1", "20"] :
-
get:会将其写到 url 上,并且以
xxx?0=1&1=20
的形式发送给后端,后端就不能简单用@RequestBody List<String> ids
来操作了 -
post:就会放进请求体中:
// 源: ["1", "20"] // 下拉展开显示是: 0:"1" 1:”20“ // 已分析视图就是: ["1", "20"]
- 的样式,则请求体里的
["1", "20"]
就可以在后端用@RequestBody List<String> ids
来操作了
- 的样式,则请求体里的
-
-
简单来讲就是:
@PathVariable —— 路径取 " /xxx " @RequestParam —— get 取 " ?xxx " @RequestBody —— post 取方法体中 "封装的"
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· winform 绘制太阳,地球,月球 运作规律
· 上周热点回顾(3.3-3.9)