当前位置: 首页 > news >正文

后端Web实战-部门管理开发

目录

1.删除部门

1.1需求分析

1.2 思路分析

1.2.1 思路说明

1.2.1 简单参数接收

1.3 代码实现

1.4 Mybatis中的#与$

2.新增部门

2.1 需求分析

2.2 思路分析

2.2.1 思路说明

2.2.2 json参数接收

2.3 代码实现

3.修改部门

3.1 查询回显

3.1.1 需求分析

3.1.2 思路分析

3.1.2.1 思路说明

3.1.2.2 路径参数接收

3.1.3 代码实现

3.2 修改数据

3.2.1 需求分析

3.2.2 思路分析

3.2.3 代码实现

3.2.4 @RequestMapping

3.3.2 功能优化

4.日志技术

4.1 概述

4.2 日志框架

4.3 Logback快速入门

4.4 Logback配置文件

4.5 Logback日志级别


1.删除部门

1.1需求分析

删除部门数据。在点击 "删除" 按钮,会根据ID删除部门数据。

需求分析之后我们要看接口文档中关于删除部门接口的描述,然后根据接口文档进行服务端接口的开发

1.2 思路分析

1.2.1 思路说明

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

  • Mapper主要负责写SQL语句
  • Service调用Mapper接口方法
  • Controller需要接收请求参数(如删除部门时id = 1),再调用Service层,最后响应结果

这里的难点在于Controller如何从前端接收请求参数?

1.2.1 简单参数接收

1.通过原始的 HttpServletRequest 对象获取请求参数

/**
* 根据ID删除部门 - 简单参数接收: 方式一 (HttpServletRequest)
*/
@DeleteMapping("/depts")
public Result delete(HttpServletRequest request){String id = request.getParameter("id");int id = Integer.parseInt(id); //需要手动进行强转System.out.println("根据ID删除部门: " + id);return Result.success();
}

2.通过Spring提供的 @RequestParam 注解,将请求参数绑定给方法形参

@DeleteMapping("/depts")
public Result delete(@RequestParam("id") Integer deptId){System.out.println("根据ID删除部门: " + deptId);return Result.success();
}

@RequestParam 注解的value属性,需要与前端传递的参数名保持一致 。

@RequestParam注解required属性默认为true,代表该参数必须传递,如果不传递将报错。 如果参数可选,可以将属性设置为false。

3.(更推荐)如果请求参数名与形参变量名相同,直接定义方法形参即可接收。(省略@RequestParam)

@DeleteMapping("/depts")
public Result delete(Integer id){System.out.println("根据ID删除部门: " + deptId);return Result.success();
}

1.3 代码实现

1). Mapper层

@Mapper
public interface DeptMapper {/*** 查询所有部门信息** @return*/@Select("select * from dept")public List<Dept> list();/*** 根据id删除部门*/@Delete("delete from dept where id = #{id}")void delete(Integer id);
}

如果mapper接口方法形参只有一个普通类型的参数,可以用#{…}, 里面的属性名可以随便写,如:#{id}、#{value}。

对于 DML 语句来说,执行完毕,也是有返回值的,返回值代表的是增删改操作,影响的记录数,所以可以将执行 DML 语句的方法返回值设置为 Integer。 但是一般开发时,是不需要这个返回值的,所以也可以设置为void。

2). Service层

/*** 业务逻辑处理层*/@Service
public class DeptServiceImpl implements DeptService {@Autowired //从IOC容器中,自动寻找bean对象,为该变量赋值--依赖注入DI的实现private DeptMapper deptMapper;//查询所有部门public List<Dept> list(){//1.调用mapper的方法,获取列表数据并返回return deptMapper.list();}//根据id删除部门public void delete(Integer id){//调用mapper的删除方法deptMapper.delete(id);}
}

3). Controller层

/** 请求处理类*/
@RestController  //等价于@Controller + @ResponseBody
public class DeptController {@Autowiredprivate DeptService deptService;/*** 查询所有部门信息* @return*/@GetMapping("/depts")public Result list() {//1.调用service获取数据List<Dept> depts = deptService.list();//3.响应数据(json格式)return Result.success(depts);}public Result delete(Integer id){System.out.println("deptId = " + id);//调用Service的删除方法deptService.delete(id);//不需要返回值return Result.success();}
}

1.4 Mybatis中的#与$

在mybatis的mapper接口中,我们定义SQL语句,参数占位符可以使用#{...}与${...}

符号说明场景优缺点
#{…}执行时,会将#{…}替换为?,生成预编译SQL,并自动设置参数值参数值传递安全、性能高 (推荐)
${…}拼接SQL。直接将参数拼接在SQL语句中,存在SQL注入问题表名、字段名动态设置时使用不安全、性能低

1). 如果我们在定义SQL语句的时候,使用的是 #{...}

最终生成的SQL语句是预编译的SQL语句。

2). 如果我们在定义SQL语句的时候,使用的是 ${...}

2.新增部门

2.1 需求分析

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

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

2.2 思路分析

2.2.1 思路说明

注意在Controller接收到的json格式请求参数只有name,但是添加部门还需要id、create_time、update_time,id我们在建表时设置了自增,所以可以不传入数据就可自增,我们需要在Service层中补全其余的基础属性,然后再调用Mapper层中的SQL语句对数据库进行操作。

2.2.2 json参数接收

在Controller中需要接受前端传递的json格式请求数据

  • JSON格式的参数,通常会使用一个实体对象进行接收(创建对象来接收数据)
  • 规则:JSON数据的键名与方法形参对象的属性名相同(如JSON数据中{"name" : "教研部"},与要创建的Dept类对象dept中的属性name名称一致),并需要使用@RequestBody注解标识

2.3 代码实现

1). Controller层

/*** 新增部门* @param dept* @return*/// @RequestBody 作用:用来接收json格式的请求参数@PostMapping("/depts")public Result add(@RequestBody Dept dept){System.out.println("dept = " + dept);//调用ServicedeptService.add(dept);return Result.success();}

2). Service层

/*** 新增部门* @param dept*/@Overridepublic void add(Dept dept) {//1.补充基础属性dept.setCreateTime(LocalDateTime.now());dept.setUpdateTime(LocalDateTime.now());//2.调用mapper的新增方法deptMapper.insert(dept);}

3). Mapper层

/*** 新增部门*/@Insert("insert into dept(name, create_time, update_time) values (#{name}, #{createTime}, #{updateTime})")void insert(Dept dept);

在mapper接口中,如果需要传递多个参数,可以把多个参数封装到一个对象中。在SQL语句中获取参数的时候,#{...}里面写的是对象的属性名【注意:是对象的属性名,不是表的字段名】

3.修改部门

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

3.1 查询回显

3.1.1 需求分析

当我们点击“编辑”的是时候,需要根据ID查询部门数据,然后用于页面回显展示(在框中显示当前部门的名称)

3.1.2 思路分析

3.1.2.1 思路说明

了解需求之后我们还需要看接口文档中关于ID查询部门的接口的描述,然后根据接口文档进行服务端接口的开发

3.1.2.2 路径参数接收

/depts/1、/depts/2这种url中传递的参数,我们称之为路径参数。那么如何接收这样的路径参数?

路径参数:通过请求URL直接传递参数,使用{...}来标识该路径参数,需要使用@PathVariable获取路径参数。

3.1.3 代码实现

1). Controller层

/*** 回显* @param id* @return*/// @PathVariable 作用:用于接收路径参数值@GetMapping("/depts/{id}")public Result getById(@PathVariable Integer id){System.out.println("id = " + id);//调用Service方法Dept dept = deptService.getById(id);return Result.success(dept);}

2). Service层

 /*** 数据回显* @param id* @return*/@Overridepublic Dept getById(Integer id) {//调用mapper的查询方法return deptMapper.selectById(id);}

3). Mapper层

 /*** 根据id查询部门数据*/@Select("select * from dept where id = #{id}")Dept selectById(Integer id);

编写完毕之后我们可以先用apifox进行调试,发现点击运行后会出现id所对应的数据:

然后可以在网页中调试:

3.2 修改数据

3.2.1 需求分析

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

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

3.2.2 思路分析

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

3.2.3 代码实现

1). Controller层

DeptController 中增加 update 方法,具体代码如下:

 @PutMapping("/depts")public Result update(@RequestBody Dept dept){System.out.println("dept = " + dept);deptService.update(dept);return Result.success();}

2). Service层

 /*** 修改部门* @param dept*/@Overridepublic void update(Dept dept) {//1.补充基础属性dept.setUpdateTime(LocalDateTime.now());//2.调用mapper的修改方法deptMapper.update(dept);}

3). Mapper层

DeptMapper 中增加 update 方法,具体代码如下:

/*** 更新部门数据*/@Update("update dept set name = #{name}, update_time = #{updateTime} where id = #{id}")void update(Dept dept);

代码编写完毕之后,我们就可以启动服务,进行测试了。

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

3.2.4 @RequestMapping

我们会发现,我们在 DeptController 中所定义的方法,所有的请求路径,都是 /depts 开头的,只要操作的是部门数据,请求路径都是 /depts 开头。

那么这个时候,我们其实是可以把这个公共的路径 /depts 抽取到类上的,那在各个方法上,就可以省略了这个 /depts 路径。 代码如下:

一个完整的请求路径,应该是类上的 @RequestMapping 的value属性 + 方法上的 @RequestMapping的value属性。

3.3.2 功能优化

随着用户的输入或者外部条件变化而变化的SQL语句,我们称为动态SQL

在上面的案例中,我们要更新部门的数据,我们所用的SQL语句是:
 

/*** 更新部门数据*/@Update("update dept set name = #{name}, update_time = #{updateTime} where id = #{id}")void update(Dept dept);

但是如果我们只想更新部门的name,而保持update_time不变,该怎么操作呢?

我们先将Service层中补全基础属性的功能 dept.setUpdateTime(LocalDateTime.now()) 注释掉

接着我们通过apifox发送请求,发现请求参数中不包含updateTime时,虽然数据能够传递成功,但是在idea中刷新数据库表格,我们会发现当前被修改部门的update_time是null,而我们要的是保持原来不变,显然简单的注释掉更新时间语句无法满足我们的需求,并且我们要的是动态SQL语句,在有updateTime数据时也能自动添加进去,那么该如何实现呢?

我们可以将之前DeptMapper中的update方法上的SQL语句换成动态SQL,此时就需要用到mtbatis的另一种方式:配置XML文件

1). 在 `src/main/resource` 目录下创建文件夹 `com/itheima/mapper`,并在其中定义配置文件,名为 `DeptMapper.xml`(在DeptMapper类名上点击alt + enter 即可实现)

2). 在DeptMapper.xml 中基于Mybatis中提供的动态SQL标签 <if> <set> 将SQL语句改造为动态的。

<?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.itheima.mapper.DeptMapper"><update id="update">update dept<set><if test="name != null and name != ''">name = #{name},</if><if test="updateTime != null">update_time = #{updateTime}</if></set>where id = #{id}</update>
</mapper>

在上述我们使用了,两个动态SQL的标签,具体含义如下:

  • <if>:用于判断条件是否成立。使用test属性进行条件判断,如果条件为true,则拼接SQL。

  • <set>:动态地在行首插入 SET 关键字,并会删掉额外的逗号。(用在update语句中)

3.3 总结接收参数的方法

controller层接收请求参数

接收普通参数:

方式一:通过HttpServletRequest对象获取(原始方式)

方式二:方法形参使用@RequestParam("请求参数名")修饰

方式三:形参变量名与请求参数名一致,即可自动封装

接收json参数:

方法形参必须是实体对象,对象中的属性名必须与json格式的请求参数对应(实体类Dept中有属性name,而json格式请求参数也有name

方法形参必须由@RequestBody修饰

接收路径参数:/xx/{id}

方法形参必须由@PathVariable修饰,形参变量名与请求参数名一致。

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中已经传递了此依赖

<dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.4.11</version>
</dependency>

2). 引入配置文件 logback.xml (资料中已经提供,拷贝进来,放在 src/main/resources 目录下)

<?xml version="1.0" encoding="UTF-8"?>
<configuration><!-- 控制台输出 --><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度  %msg:日志消息,%n是换行符 --><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}-%msg%n</pattern></encoder></appender><!-- 日志输出级别 --><root level="ALL"><appender-ref ref="STDOUT" /></root>
</configuration>

3). 记录日志:定义日志记录对象Logger,记录日志

public class LogTest {//定义日志记录对象private static final Logger log = LoggerFactory.getLogger(LogTest.class);@Testpublic void testLog(){log.debug("开始计算...");int sum = 0;try {int[] nums = {1, 5, 3, 2, 1, 4, 5, 4, 6, 7, 4, 34, 2, 23};for (int i = 0; i <= nums.length; i++) {sum += nums[i];}} catch (Exception e) {log.error("程序运行出错", e);}log.info("计算结果为: "+sum);log.debug("结束计算...");}}

运行单元测试,可以在控制台中看到输出的日志,如下所示:

我们可以看到在输出的日志信息中,不仅输出了日志的信息,还包括:日志的输出时间、线程名、具体在那个类中输出的。

4.4 Logback配置文件

Logback日志框架的配置文件叫 logback.xml

该配置文件是对Logback日志框架输出的日志进行控制的,可以来配置输出的格式、位置及日志开关等。

常用的两种输出日志的位置:控制台、系统文件。

1). 如果需要输出日志到控制台。添加如下配置:

<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><!--格式化输出:%d 表示日期,%thread 表示线程名,%-5level表示级别从左显示5个字符宽度,%msg表示日志消息,%n表示换行符 --><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}-%msg%n</pattern></encoder>
</appender>

2). 如果需要输出日志到文件。添加如下配置:

<!-- 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"><!-- 日志文件输出的文件名, %i表示序号 --><FileNamePattern>D:/tlias-%d{yyyy-MM-dd}-%i.log</FileNamePattern><!-- 最多保留的历史日志文件数量 --><MaxHistory>30</MaxHistory><!-- 最大文件大小,超过这个大小会触发滚动到新文件,默认为 10MB --><maxFileSize>10MB</maxFileSize></rollingPolicy><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><!--格式化输出:%d 表示日期,%thread 表示线程名,%-5level表示级别从左显示5个字符宽度,%msg表示日志消息,%n表示换行符 --><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}-%msg%n</pattern></encoder>
</appender>

3). 日志开关配置 (开启日志(ALL),取消日志(OFF))

<!-- 日志输出级别 -->
<root level="ALL"><!--输出到控制台,如果不想要的话注释掉即可--><appender-ref ref="STDOUT" /><!--输出到文件--><appender-ref ref="FILE" />
</root>

4.5 Logback日志级别

日志级别指的是日志信息的类型,日志都会分级别,常见的日志级别如下(优先级由低到高):

日志级别说明记录方式
trace追踪,记录程序运行轨迹 【使用很少】log.trace("...")
debug调试,记录程序调试过程中的信息,实际应用中一般将其视为最低级别 【使用较多】log.debug("...")
info记录程序运行的重要信息,如:数据库连接、网络连接、io操作 【使用较多】log.info("...")
warn警告信息,可能会发生问题 【使用较多】log.warn("...")
error错误信息 【使用较多】log.error("...")

可以在配置文件logback.xml中,灵活的控制输出那些类型的日志。(大于等于配置的日志级别的日志才会输出)

<!-- 日志输出级别 -->
<root level="info"><!--输出到控制台--><appender-ref ref="STDOUT" /><!--输出到文件--><appender-ref ref="FILE" />
</root>

http://www.dtcms.com/a/351321.html

相关文章:

  • BA 楼宇自控系统 + AI:重构楼宇设备管理的 “智能决策” 体系
  • 『专利好药用力心脑血管健康』——爱上古中医(28)(健康生活是coder抒写优质代码的前提条件——《黄帝内经》伴读学习纪要)
  • 阿里云 ECS 可观测性最佳实践
  • 【Canvas与旗帜】上三常之中国
  • PMP项目管理知识点-⑧ 项⽬质量管理
  • 【传奇开心果系列】Flet框架实现的图形化界面的PDF转word转换器办公小工具自定义模板
  • sed截取慢SQL大文件并导出指定时间范围内容
  • 数据结构(时空复杂度)
  • VMware 中 Ubuntu 右上角网络图标消失的 5 种终极修复方案
  • 腾讯开源OpenTenBase深度实践:企业级分布式HTAP数据库部署全攻略
  • 【URP】Unity超分辨率优化实践
  • 【yocto】Yocto Project 核心:深入了解.bbclass文件
  • 云蝠智能 Voice Agent:多语言交互时代的AI智能语音呼叫
  • 病理软件Cellprofiler使用教程
  • 【系统编程】线程控制原语
  • 半小时打造七夕传统文化网站:Qoder AI编程实战记录
  • Ansible配置文件
  • 2025第五届人工智能、自动化与高性能计算国际会议 (AIAHPC 2025)
  • YUM配置
  • 适配欧拉操作系统
  • 高频面试题:说一下线程池吧?(线程池原理,核心参数,创建方式,应用场景都要说到才能让面试官心服口服)
  • 什么是AQS?
  • Xposed框架实战指南:从原理到你的第一个模块
  • R语言使用随机森林对数据进行插补
  • 【Java基础】Java数据结构深度解析:Array、ArrayList与LinkedList的对比与实践
  • 【HarmonyOS NEXT】打包鸿蒙应用并发布到应用市场
  • 构建生产级 RAG 系统:从数据处理到智能体(Agent)的全流程深度解析
  • Linux 网络数据收发全栈工具书:从 nc、socat 到 iperf3 的 Buildroot 路径与跨平台实战
  • 开心实习之第三十二天
  • Python爬虫实战:Uiautomator2 详解与应用场景