Day09-后端Web实战——部分管理开发&Logback日志技术

打印 上一主题 下一主题

主题 898|帖子 898|积分 2694

部分管理开发

1. 删除部分

1.1 需求分析

删除部分数据。在点击 “删除” 按钮,会根据ID删除部分数据。

了解了需求之后,我们再看看接口文档中,关于删除部分的接口的形貌,然后根据接口文档进行服务端接口的开发。

1.2 思绪分析

1.2.1 思绪说明

明确了删除部分的需求之后,再来梳理一下实现该功能时,三层架构每一层的职责:

1.2.1 简单参数接收

我们看到,在controller中,需要接收前端传递的哀求参数。 那接下来,我们就先来看看在服务器端的Controller程序中,如何获取这类简单参数。 详细的方案有如下三种:
方案一:通过原始的 HttpServletRequest 对象获取哀求参数
  1. /**
  2. * 根据ID删除部门 - 简单参数接收: 方式一 (HttpServletRequest)
  3. */
  4. @DeleteMapping("/depts")
  5. public Result delete(HttpServletRequest request){
  6.     String idStr = request.getParameter("id");
  7.     int id = Integer.parseInt(idStr);
  8.     System.out.println("根据ID删除部门: " + id);
  9.     return Result.success();
  10. }
复制代码
这种方案实现较为繁琐,而且还需要进行手动类型转换。
方案二:通过Spring提供的 @RequestParam 注解,将哀求参数绑定给方法形参
  1. @DeleteMapping("/depts")
  2. public Result delete(@RequestParam("id") Integer deptId){
  3.     System.out.println("根据ID删除部门: " + deptId);
  4.     return Result.success();
  5. }
复制代码
@RequestParam 注解的value属性,需要与前端传递的参数名保持同等 。
   @RequestParam注解required属性默认为true,代表该参数必须传递,如果不传递将报错。 如果参数可选,可以将属性设置为false。
  方案三:如果哀求参数名与形参变量名雷同,直接定义方法形参即可接收。(省略@RequestParam)
  1. @DeleteMapping("/depts")
  2. public Result delete(Integer id){
  3.     System.out.println("根据ID删除部门: " + deptId);
  4.     return Result.success();
  5. }
复制代码
对于以上的这三种方案呢,我们保举第三种方案。
1.3 代码实现

1). Mapper层
  1.     /**
  2.      * 根据ID删除部门数据
  3.      *
  4.      * # 符号: 占位符,会被 ?替换为预编译的SQL(推荐); 通常用于字段值的替换.
  5.      * $ 符号: 字符串拼接符号,会将参数直接拼接在SQL语句中(不推荐); 如果需要动态设置表名, 字段名时, 必须使用$符号.
  6.      */
  7.     @Delete("delete from dept where id = #{id}")
  8.     void delete(Integer id);
复制代码
  如果mapper接口方法形参只有一个普通类型的参数,#{…} 内里的属性名可以随便写,如:#{id}、#{value}。
  对于 DML 语句来说,执行完毕,也是有返回值的,返回值代表的是增删改操作,影响的记录数,所以可以将执行 DML 语句的方法返回值设置为 Integer。 但是一样寻常开发时,是不需要这个返回值的,所以也可以设置为void。
2). Service层
  1. @Override
  2. public void delete(Integer id) {
  3.     //Integer deleted = deptMapper.delete(id);
  4.     //System.out.println("删除数据的结果为: " + deleted);
  5.     deptMapper.delete(id);
  6. }
复制代码
3). Controller层
  1. @DeleteMapping("/depts")
  2. public Result delete(Integer id){
  3.     System.out.println("根据ID删除部门: " + id);
  4.     deptService.delete(id);
  5.     return Result.success();
  6. }
复制代码
代码编写完毕之后,我们就可以启动服务,进行测试了。

1.4 Mybatis中的#与$

在mybatis的mapper接口中,我们定义SQL语句,参数占位符可以利用 #{...} 与 ${...},那详细什么区别呢?
符号说明场景优缺点#{…}执行时,会将#{…}替换为?,天生预编译SQL,并主动设置参数值参数值传递安全、性能高 (保举)${…}拼接SQL。直接将参数拼接在SQL语句中,存在SQL注入问题表名、字段名动态设置时利用不安全、性能低 1). 如果我们在定义SQL语句的时间,利用的是 #{...}

我们看到,终极天生的SQL语句是预编译的SQL语句。
2). 如果我们在定义SQL语句的时间,利用的是 ${...}

2. 新增部分

2.1 需求分析

点击 “新增部分” 的按钮之后,弹出新增部分表单,填写部分名称之后,点击确定之后,保存部分数据。

了解了需求之后,我们再看看接口文档中,关于新增部分的接口的形貌,然后根据接口文档进行服务端接口的开发 。

2.2 思绪分析

2.2.1 思绪说明

明确了新增部分的需求之后,再来梳理一下实现该功能时,三层架构每一层的职责:

2.2.2 json参数接收

我们看到,在controller中,需要接收前端传递的哀求参数。 那接下来,我们就先来看看在服务器端的Controller程序中,如何获取json格式的参数。


  • JSON格式的参数,通常会利用一个实体对象进行接收 。
  • 规则:JSON数据的键名与方法形参对象的属性名雷同,并需要利用@RequestBody注解标识。

2.3 代码实现

1). Controller层
在DeptController中增长方法add,详细代码如下:
  1. /**
  2. * 添加部门 - json格式参数接收
  3. */
  4. @Log
  5. @PostMapping
  6. public Result add(@RequestBody Dept dept){
  7.     System.out.println("添加部门: " + dept);
  8.     deptService.add(dept);
  9.     return Result.success();
  10. }
复制代码
2). Service层
在DeptServiceImpl中增长add方法,完成添加部分的操作,详细代码如下:
  1. @Override
  2. public void add(Dept dept) {
  3.     dept.setCreateTime(LocalDateTime.now());
  4.     dept.setUpdateTime(LocalDateTime.now());
  5.     deptMapper.add(dept);
  6. }
复制代码
3). Mapper层
在 DeptMapper 中增长方法add,完成添加部分的操作详细代码如下:
  1. /**
  2. * 添加部门数据 - 传递多个参数时,可以把多个参数封装到一个对象中 , 然后通过 #{属性名} 来获取对象属性
  3. */
  4. @Insert("insert into dept(name, create_time, update_time) values(#{name}, #{createTime}, #{updateTime})")
  5. void add(Dept dept);
复制代码
如果在mapper接口中,需要传递多个参数,可以把多个参数封装到一个对象中。 在SQL语句中获取参数的时间,#{...} 内里写的是对象的属性名【注意是属性名,不是表的字段名】。
代码编写完毕之后,我们就可以启动服务,进行测试了。


3. 修改部分

对于任何业务的修改功能来说,一样寻常都会分为两步进行:查询回显、修改数据。

3.1 查询回显

3.1.1 需求分析

当我们点击 “编辑” 的时间,需要根据ID查询部分数据,然后用于页面回显展示。

3.1.2 思绪分析

3.1.2.1 思绪说明

明确了根据ID查询部分的需求之后,再来梳理一下实现该功能时,三层架构每一层的职责:

了解了需求之后,我们再看看接口文档中,关于根据ID查询部分的接口的形貌,然后根据接口文档进行服务端接口的开发 。

3.1.2.2 路径参数接收

/depts/1,/depts/2 这种在url中传递的参数,我们称之为路径参数。 那么如何接收这样的路径参数呢 ?
路径参数:通过哀求URL直接传递参数,利用{…}来标识该路径参数,需要利用 @PathVariable 获取路径参数。如下所示:

  1. /**
  2. * 根据ID查询部门数据
  3. * @return
  4. */
  5. @GetMapping("/{id}")
  6. public Result getInfo(@PathVariable Integer id){
  7.     System.out.println("根据ID查询部门数据: " + id);
  8.     return Result.success();
  9. }
复制代码
3.1.2.3 思考

在url中是否可以携带多个路径参数呢 ,如:/depts/1/0 。是可以这么传递的,详细的接收方式如下:
  1. @GetMapping("/depts/{id}/{sta}")
  2. public Result getInfo(@PathVariable Integer id, @PathVariable Integer sta){
  3.         //...
  4. }
复制代码
3.1.3 代码实现

1). Controller层
在 DeptController 中增长 getInfo 方法,详细代码如下:
  1. /**
  2. * 根据ID查询部门数据
  3. */
  4. @GetMapping("/{id}")
  5. public Result getInfo(@PathVariable Integer id){
  6.     System.out.println("根据ID查询部门数据: " + id);
  7.     Dept dept = deptService.getInfo(id);
  8.     return Result.success(dept);
  9. }
复制代码
2). Service层
在 DeptServiceImpl 中增长 getInfo 方法,详细代码如下:
  1. @Override
  2. public Dept getInfo(Integer id) {
  3.         return deptMapper.getById(id);
  4. }
复制代码
3). Mapper层
在 DeptMapper 中增长 getById 方法,详细代码如下:
  1. /**
  2. * 根据ID查询部门数据
  3. */
  4. @Select("select id, name, create_time, update_time from dept where id = #{id}")
  5. Dept getById(Integer id);
复制代码
代码编写完毕之后,我们就可以启动服务,进行测试了。

3.2 修改数据

3.2.1 需求分析

查询回显回来之后,就可以对部分的信息进行修改了,修改完毕之后,点击确定,此时,就需要根据ID修改部分的数据。

了解了需求之后,我们再看看接口文档中,关于修改部分的接口的形貌,然后根据接口文档进行服务端接口的开发 。

3.2.2 思绪分析

参照接口文档,梳理三层架构每一层的职责:

通过接口文档,我们可以看到前端传递的哀求参数是json格式的哀求参数,在Controller的方法中,我们可以通过 @RequestBody 注解来接收,并将其封装到一个对象中。
3.2.3 代码实现

1). Controller层
在 DeptController 中增长 update 方法,详细代码如下:
  1. /**
  2. * 修改部门数据
  3. */
  4. @PutMapping
  5. public Result update(@RequestBody Dept dept){
  6.     System.out.println("修改部门数据: " + dept);
  7.     deptService.update(dept);
  8.     return Result.success();
  9. }
复制代码
2). Service层
在 DeptServiceImpl 中增长 update 方法。 由于是修改操作,每一次修改数据,都需要更新updateTime。所以,详细代码如下:
  1. @Override
  2. public void update(Dept dept) {
  3.     dept.setUpdateTime(LocalDateTime.now());
  4.     deptMapper.update(dept);
  5. }
复制代码
3). Mapper层
在 DeptMapper 中增长 update 方法,详细代码如下:
  1. /**
  2. * 根据ID更新部门数据
  3. */
  4. @Update("update dept set name = #{name}, update_time = #{updateTime} where id = #{id}")
  5. void update(Dept dept);
复制代码
代码编写完毕之后,我们就可以启动服务,进行测试了。

修改完成之后,我们可以看到最新的数据,如下:

3.2.4 @RequestMapping

到此呢,关于基本的部分的增删改查功能,我们已经实现了。 我们会发现,我们在 DeptController 中所定义的方法,所有的哀求路径,都是 /depts 开头的,只要操作的是部分数据,哀求路径都是 /depts 开头。
那么这个时间,我们其实是可以把这个公共的路径 /depts 抽取到类上的,那在各个方法上,就可以省略了这个 /depts 路径。 代码如下:

   一个完备的哀求路径,应该是类上的 @RequestMapping 的value属性 + 方法上的 @RequestMapping的value属性。
  3.3 功能优化

3.3.1 问题分析

我们刚才在修改部分数据的时间,是根据ID修改所有的部分数据信息,详细的代码如下:

那我们在更新部分数据时,如何做到只更新其中的某一个或某几个字段呢?好比:
我只想根据ID更新部分名称,不更新updateTime。 按照我们当前编写的SQL语句,就会将update_time字段置为null。

而正常的SQL应该是不更新 update_time,如下:

也就是说,传递了对应的数据值,我就更新对应的字段。如果没有传递,则不更新对应的字段。也就说,这个SQL语句,不是固定的,是随着条件动态变化的。 那要想实现这样的结果,就需要用到Mybatis中的动态SQL
3.3.2 功能优化

随着用户的输入或外部条件的变化而变化的SQL语句,我们称为 动态SQL
我们可以将之前在 DeptMapper 中update方法上定义的SQL语句,换成动态SQL。 详细操作步骤如下:
1). 在 src/main/resource 目次下创建文件夹 com/itheima/mapper,并在其中定义配置文件,名为 DeptMapper.xml

2). 在DeptMapper.xml 中基于Mybatis中提供的动态SQL标签 <if> <set> 将SQL语句改造为动态的。
  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper
  3.         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4.         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace="com.itheima.mapper.DeptMapper">
  6.     <!--动态更新部门数据-->
  7.     <update id="update">
  8.         update dept
  9.         <set>
  10.             <if test="name != null and name != ''">
  11.                 name = #{name},
  12.             </if>
  13.             <if test="updateTime != null">
  14.                 update_time = #{updateTime}
  15.             </if>
  16.         </set>
  17.         where id = #{id}
  18.     </update>
  19. </mapper>
复制代码
在上述我们利用了,两个动态SQL的标签,详细含义如下:


  • <if>:用于判断条件是否建立。利用test属性进行条件判断,如果条件为true,则拼接SQL。
  • <set>:动态地在行首插入 SET 关键字,并会删掉额外的逗号。(用在update语句中)
4. 日志技术

4.1 概述



  • 什么时日志?

    • 日志就好比生活中的日志,可以随时随地记录你生活中的点点滴滴。
    • 程序中的日志,是用来记录应用程序的运行信息、状态信息、错误信息的。

  • 为什么要在程序中记录日志呢?

    • 便于追踪应用程序中的数据信息、程序的执行过程。
    • 便于对应用程序的性能进行优化。
    • 便于应用程序出现问题之后,排查问题,解决问题。
    • 便于监控体系的运行状态。
    • … …

  • 之前我们编写程序时,也可以通过 System.out.println(...) 来输出日志,为什么我们还要学习单独的日志技术呢?

    • 这是因为,如果通过 System.out.println(...)  来记录日志,会存在以下几点问题:

      • 硬编码。所有的记录日志的代码,都是硬编码,没有办法做到机动控制,要想不输出这个日志了,只能删除掉记录日志的代码。
      • 只能输出日志到控制台。
      • 不便于程序的扩展、维护。


所以,在现在的项目开发中,我们一样寻常都会利用专业的日志框架,来解决这些问题。
4.2 日志框架


JUL: 这是JavaSE平台提供的官方日志框架,也被称为JUL。配置相对简单,但不够机动,性能较差。
Log4j: 一个流行的日志框架,提供了机动的配置选项,支持多种输出目标。
Logback: 基于Log4j升级而来,提供了更多的功能和配置选项,性能由于Log4j。
Slf4j:(Simple Logging Facade for Java)简单日志门面,提供了一套日志操作的尺度接口及抽象类,允许应用程序利用差别的底层日志框架。
4.3 Logback快速入门

1). 准备工作:引入logback的依靠(springboot中无需引入,在springboot中已经传递了此依靠
  1. <dependency>
  2.     <groupId>ch.qos.logback</groupId>
  3.     <artifactId>logback-classic</artifactId>
  4.     <version>1.4.11</version>
  5. </dependency>
复制代码
2). 引入配置文件 logback.xml ,放在 src/main/resources 目次下
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <configuration>
  3.         <!-- 控制台输出 -->
  4.         <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
  5.                 <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
  6.                         <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度  %msg:日志消息,%n是换行符 -->
  7.                         <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}-%msg%n</pattern>
  8.                 </encoder>
  9.         </appender>
  10.         <!-- 日志输出级别 -->
  11.         <root level="ALL">
  12.                 <appender-ref ref="STDOUT" />
  13.         </root>
  14. </configuration>
复制代码
3). 记录日志:定义日志记录对象Logger,记录日志
  1. @Slf4j //该注解可以替代定义日志记录对象代码
  2. public class LogTest {
  3.    
  4.     //定义日志记录对象
  5.     //private static final Logger log = LoggerFactory.getLogger(LogTest.class);
  6.     @Test
  7.     public void testLog(){
  8.         log.debug("开始计算...");
  9.         int sum = 0;
  10.         try {
  11.             int[] nums = {1, 5, 3, 2, 1, 4, 5, 4, 6, 7, 4, 34, 2, 23};
  12.             for (int i = 0; i <= nums.length; i++) {
  13.                 sum += nums[i];
  14.             }
  15.         } catch (Exception e) {
  16.             log.error("程序运行出错", e);
  17.         }
  18.         log.info("计算结果为: "+sum);
  19.         log.debug("结束计算...");
  20.     }
  21. }
复制代码
运行单位测试,可以在控制台中看到输出的日志,如下所示:

我们可以看到在输出的日志信息中,不仅输出了日志的信息,还包括:日志的输出时间、线程名、详细在谁人类中输出的。
4.4 Logback配置文件

Logback日志框架的配置文件叫 logback.xml 。
该配置文件是对Logback日志框架输出的日志进行控制的,可以来配置输出的格式、位置及日志开关等。
常用的两种输出日志的位置:控制台、体系文件。
1). 如果需要输出日志到控制台。添加如下配置:
  1. <!-- 控制台输出 -->
  2. <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
  3.     <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
  4.             <!--格式化输出:%d 表示日期,%thread 表示线程名,%-5level表示级别从左显示5个字符宽度,%msg表示日志消息,%n表示换行符 -->
  5.             <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}-%msg%n</pattern>
  6.     </encoder>
  7. </appender>
复制代码
2). 如果需要输出日志到文件。添加如下配置:
  1. <!-- 按照每天生成日志文件 -->
  2. <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
  3.     <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
  4.         <!-- 日志文件输出的文件名, %i表示序号 -->
  5.         <FileNamePattern>D:/tlias-%d{yyyy-MM-dd}-%i.log</FileNamePattern>
  6.         <!-- 最多保留的历史日志文件数量 -->
  7.         <MaxHistory>30</MaxHistory>
  8.         <!-- 最大文件大小,超过这个大小会触发滚动到新文件,默认为 10MB -->
  9.         <maxFileSize>10MB</maxFileSize>
  10.     </rollingPolicy>
  11.     <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
  12.         <!--格式化输出:%d 表示日期,%thread 表示线程名,%-5level表示级别从左显示5个字符宽度,%msg表示日志消息,%n表示换行符 -->
  13.         <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}-%msg%n</pattern>
  14.     </encoder>
  15. </appender>
复制代码
3). 日志开关配置 (开启日志(ALL),取消日志(OFF))
  1. <!-- 日志输出级别 -->
  2. <root level="ALL">
  3.     <!--输出到控制台-->
  4.     <appender-ref ref="STDOUT" />
  5.     <!--输出到文件-->
  6.     <appender-ref ref="FILE" />
  7. </root>
复制代码
4.5 Logback日志级别

日志级别指的是日志信息的类型,日志都会分级别,常见的日志级别如下(优先级由低到高):
日志级别说明记录方式trace追踪,记录程序运行轨迹 【利用很少】log.trace(“…”)debug调试,记录程序调试过程中的信息,实际应用中一样寻常将其视为最低级别 【利用较多】log.debug(“…”)info记录程序运行的重要信息,如:数据库连接、网络连接、io操作 【利用较多】log.info(“…”)warn告诫信息,可能会发生问题 【利用较多】log.warn(“…”)error错误信息 【利用较多】log.error(“…”) 可以在配置文件logback.xml中,机动的控制输出那些类型的日志。(大于等于配置的日志级别的日志才会输出)
  1. <!-- 日志输出级别 -->
  2. <root level="info">
  3.     <!--输出到控制台-->
  4.     <appender-ref ref="STDOUT" />
  5.     <!--输出到文件-->
  6.     <appender-ref ref="FILE" />
  7. </root>
复制代码
完备版
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <configuration>
  3.         <!-- 控制台输出 -->
  4.         <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
  5.                 <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
  6.                         <!--格式化输出:%d 表示日期,%thread 表示线程名,%-5level表示级别从左显示5个字符宽度,%msg表示日志消息,%n表示换行符 -->
  7.                         <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}-%msg%n</pattern>
  8.                 </encoder>
  9.         </appender>
  10.         <!-- 按照每天生成日志文件 -->
  11.         <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
  12.                 <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
  13.                         <!-- 日志文件输出的文件名, %i表示序号 -->
  14.                         <FileNamePattern>D:/tlias-%d{yyyy-MM-dd}-%i.log</FileNamePattern>
  15.                         <!-- 最多保留的历史日志文件数量 -->
  16.                         <MaxHistory>30</MaxHistory>
  17.                         <!-- 最大文件大小,超过这个大小会触发滚动到新文件,默认为 10MB -->
  18.                         <maxFileSize>10MB</maxFileSize>
  19.                 </rollingPolicy>
  20.                 <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
  21.                         <!--格式化输出:%d 表示日期,%thread 表示线程名,%-5level表示级别从左显示5个字符宽度,%msg表示日志消息,%n表示换行符 -->
  22.                         <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}-%msg%n</pattern>
  23.                 </encoder>
  24.         </appender>
  25.         <!-- 日志输出级别 -->
  26.         <root level="info">
  27.                 <appender-ref ref="STDOUT" />
  28.                 <appender-ref ref="FILE" />
  29.         </root>
  30. </configuration>
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

慢吞云雾缓吐愁

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表