数据库、后端(主要控制层注解的使用场景)

发布时间 2023-12-11 22:22:02作者: 朱呀朱~

数据库

  • 一个系列功能的表名尽量统一规范,如: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 文件位置方式
    1. 都放在 resources 下严格与 dao 同目录下的地方
    2. 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 里 数据库列名 = #{传来的参数}
  • service 层里同样接口,impl 继承
    • @Service 绑定在 impl 中,@Autowired 自动注入 dao 层的接口,impl 若无需要的额外操作就直接 return dao 的方法名即可
    • 小提一嘴:不用 mapper.xml 方式时,就是给 dao 的 impl 加上 @Repository 注解

实用方式

  • 最重要的一点:千万不要按照自己的原来编写的风格和目录样式来

  • entity

    • 推荐使用 @Data —— lombok 依赖自动生成 toStringequalshashCodegettersetter 等方法
    • 实体类里可以划分出多个文件夹存放不同情况下用的实体类,如 PO、DTO、VO、BO 等,根据统一需求来,一般情况下只用 entity 也无妨
  • 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", 传参值);

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 处呈现
  • 这里就不得不提一些概念了:

    • 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 取方法体中 "封装的"