04 后端增删改查【小白入门SpringBoot + Vue3】

news2024/12/28 5:59:22

项目笔记,教学视频来源于B站青戈
https://www.bilibili.com/video/BV1H14y1S7YV

保证前面的都功能都实现后,接着往下走。

查 +分页

接下来,实现前端页面分页功能。

前端分页组件

打开elementplus官网,找到合适的分页组件,其对应代码如下

<template>
  <el-pagination background layout="prev, pager, next" :total="1000" />
</template>

放到前端的表格组件下方,加个div包裹。

js加个变量,因为total总记录数量,应该是从数据库拿到的,是动态的。

<!--    分页组件-->
    <div>
        <el-pagination background layout="prev, pager, next" :total="total" />
    </div>


const total = ref(0) //总记录数,初始为0

然后打开后端项目,实现分页查询,需要与SQL语句配合,而且浏览器的路由也可以修改后缀。

后端SQL语句修改

首先是数据库的SQL语句,复制上个SQL代码,稍加修改,让其实现分页,借助LIMIT这个分页函数后面的两个参数,第一个是开始序号,第二个是每页放置多少条记录。

select * from user where name like%%or date like%%or address like%%or user_no like%%LIMIT 1,2

接着尝试修改后端的SQL代码,

在UserMapper新增SQL代码;

注意@Param在导入包的时候,要选择有apache单词的包,选错会导致报错。

@Select("select * from user where name like concat('%',#{name},'%')  or date like concat('%',#{name},'%') or address like concat('%',#{name},'%') or user_no like concat('%',#{name},'%')  limit #{start}, #{pageSize}") 
List<User> selectPage(@Param("name") String name,@Param("start") Integer start,@Param("pageSize") Integer pageSize); //三个参数

在UserController这个文件添加以下代码

    @GetMapping("/page")    //表示访问到这个函数返回值,其路径是【localhost:9090/user/page】
    public Result selectPage(@RequestParam String name,@RequestParam Integer start,@RequestParam Integer pageSize){
        List<User> userList = userMapper.selectPage( name,start,pageSize);
        return Result.success(userList);
    }

前端SQL相关修改

后端配置得差不多,前端也进行相应修改,来到HomeView.vue ,把数据请求的路由更改成与后端SQL语句还参数一致的

const load = () =>{
  request.get('user/page?name=' + input.value + '&start=1&pageSize=2').then(res =>{ 
    // request.get('user/all?name=' + input.value).then(res =>{ //name改成input
    console.log(res)
    state.tableData = res.data
  })
}

然后数据库、前端、后端,都运行,打开前端项目生成的本地网址,若表格出现数据,则这一步成功。

但是上面的分页参数&start=1&pageSize=2,是静态的写死的,我们希望这个分页参数是可以根据用户手动选择的,于是要考虑运用分页组件的参数。

找到elementplus前端分页组件的代码,加上两个数据绑定

原来
<el-pagination  layout="prev, pager, next" :total="total"/>


修改
<el-pagination  layout="prev, pager, next" v-model:current-page="pageNum" v-model:page-size="pageSize" :page-size="[1,2,5]" :total="total"/>

在js加上这两个变量,为了把页码的两个参数变成动态的,初步修改如下

const pageNum = ref(1) //页码。最小的是1
const pageSize = ref(1) //页码大小,实际上数据库无记录可以显示零条,但是理论上最起码显示一条

const load = () => {
  let start = pageNum.value - 1  //数据库的记录序号从0开始,但是前端页面数据从1开始
  request.get('user/page?name=' + input.value + '&start='+ start +'&pageSize=' + pageSize.value).then(res => { 
      //省略代码
  })
}

去看前端的分页,但是发现没反应。

首先因为组件的total 变量数值默认是 零,但数据库目前的总数量是2,故手动修改。

const total = ref(2) //总记录数

其次,分页没有触发load函数故,回到elementplus官网,找到分页切换触发的两个函数,所以修改如下

原来(其他属性省略没写)
 <el-pagination layout="prev, pager, next"  / >

修改后
 <el-pagination  layout="sizes,prev, pager, next"  @size-change="load"  @current-change="load"/>

补充:

layout属性有多个属性值,它们有不同的功能,从左到右依次为

  1. total,表示后端数据的总条数,即页面上的 “ 共400条 ”
  2. sizes,表示每页能容纳多少条数据,即页面上的下拉选框 “ 100条/页 ”
  3. prev,表示向前翻一页,即页面上的 “ < ”
  4. pager,表示指定翻到哪一页,即页面上的 “ 1 2 3 4 ”
  5. next,表示向后翻一页,即页面上的 “ > ”
  6. jumper,表示直接跳去哪一页,即页面上的 “ 前往 3 页 ”

二个核心事件

  1. @size-change事件,当你使用下拉选框改变page-size属性时,它能监听到page-size属性的变化,并立刻将最新的值自动传给的事件处理函数,你甚至无需给它手动传参
  2. @current-change事件,当你改变current-page属性时,它能监听到current-page属性的变化,并立刻将最新的值自动传给相应的事件处理函数,你甚至无需给它手动传参

然后,发现,分页组件的文案都是英文,想改成中文,可以使用elementplus的国际化功能。来到前端项目的main.js ,添加下面的代码

import zhCn from 'element-plus/dist/locale/zh-cn.min'

// app.use(ElementPlus)  //原来的
app.use(ElementPlus,{  //修改的
    locale: zhCn,
})

一个新的问题,随着数据库记录数量的增多,发现最后一条记录会被吞掉,原因是数据库页的起始序号计算有问题

原来
const load = () => {
  let start = pageNum.value - 1  //数据库的记录序号从0开始,但是前端页面数据从1开始
  //省略代码  
}

修改
const load = () => {
  let start = (pageNum.value - 1)*pageSize.value 
  //数据库的记录序号从0开始,但是前端页面数据从1开始,为了避免吞数据,需要乘每页的数量
  //省略代码  
}

动态total 修改

下一个问题,total的数值,目前还是静态写死的,为了改成动态的,需要从数据库中查出具体数值并显示到前端上。

第一步,后端项目,在UserMapper文件新增代码(复制粘贴上面的SQL语句,然后修改)如下

@Select("select count(id) from user where name like concat('%',#{name},'%')  or date like concat('%',#{name},'%') or address like concat('%',#{name},'%') or user_no like concat('%',#{name},'%')")  
Integer selectTotal(@Param("name") String name); //一个参数,函数返回值是integer类型

第二步,后端项目,UseController文件,新增代码,主要是增加total这个变量,把数值顺利传递到前端,修改如下

原来
@GetMapping("/page")    //表示访问到这个函数返回值,其路径是【localhost:9090/user/page】
public Result selectPage(@RequestParam String name,@RequestParam Integer start,@RequestParam Integer pageSize){
   List<User> userList = userMapper.selectPage( name,start,pageSize);
   return Result.success(userList);
}


修改
@GetMapping("/page")    //表示访问到这个函数返回值,其路径是【localhost:9090/user/page】
public Result selectPage(@RequestParam String name,@RequestParam Integer start,@RequestParam Integer pageSize){
   List<User> userList = userMapper.selectPage( name,start,pageSize);

   Integer total = userMapper.selectTotal(name);
   Map<String,Object> map= new HashMap<>();
   map.put("list",userList);
   map.put("total",total);
        
   return Result.success(map);
}    

然后,重新访问前端页面,发现表格里面的数据没有显示出来。

为什么?因为刚才修改的Controller文件,把返回的数据包裹成map类型(key:value),里面有两个key,如下图,data里面有两个对象,需要进一步访问。
在这里插入图片描述

所以,前端访问data的代码要稍加修改,在前端项目,HomeView.vue修改

原来
<el-pagination layout="sizes,prev, pager, next"/>
    
const load = () => {
  //省略代码
  ({
    state.tableData = res.data
  })
}


修改(layout加上total这个布局,展示一共有多少条数据)
<el-pagination layout="sizes,prev, pager, next,total"/>
    
const load = () => {
  //省略代码
  ({
    state.tableData = res.data.list  //修改这里
    total.value = res.data.total  //添加这个语句  
  })
}

补充说明:layout等号右边的单词,排序也有是有讲究的,会根据你排放的顺序调整哦



刚开始前端的增删改查,都是在前端单方面的简易模拟,接下来与数据库挂钩。

先做增加功能。

增加的数据中,有“日期”信息,之前为了简单,只用了字符串String类型,但是实际上是用专门的日期组件,更加方便美观。

在elementplus找到日期选择器组件,复制粘贴然后根据自身需求修改代码,如下

<el-date-picker v-model="state.form.data" type="date" placeholder="选择一个日期" />

来到后端,把数据库SQL语句写好,来到UseMapper,新增代码如下

@Insert("insert into user(name,date,address,user_no) values( #{name} , #{date} ,#{address},#{userNo}  )")
void insert(User user);

来到UseController,新增代码如下

    @PostMapping("/save")
    public Result save(@RequestBody User user){
        userMapper.insert(user);
        return Result.success();
    }

至此,后端代码ok,需要修改前端的相应代码。

来到前端项目HomeViews.vue ,修改save()函数

原来
const save = () => {
  if (globalIndex.value > -1) { //编辑数据
    state.tableData[globalIndex.value] = state.form;
    globalIndex = ref(-1)
  } else { //新增数据
    // 向表格添加数据
    state.tableData.push(state.form);
  }
  // 关闭弹窗
  dialogFormVisible.value = false;
}


修改
const save = () => {
  console.log(state.form)//打印当前弹窗的数据内容
  request.post('/user/save',state.form).then(res=>{
    if (res.code === '200'){
      ElMessage.success("保存成功")
      // 关闭弹窗
      dialogFormVisible.value = false;
    }else {
      ElMessage.error(res.msg)
    }
  },err=>{
    console.log("发送post请求失败:",err)  //现在是后端返回错误
  }
  )
}

但是运行的时候出现错误,如下

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class com.example.springboot.entity.User]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.example.springboot.entity.User` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 1, column: 2]] with root cause

找了师兄才解决这个问题

在后端UseMapper添加三个注解,并且引入相关的包

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor

接着就可以在数据库中查看,数据是否被增加进去。

发现,虽然添加成功了,但是添加的时间格式有问题

在这里插入图片描述

故需要修改时间格式,解决很简单,elementplus有时间格式的属性,修改如下

原来
          <el-date-picker
              v-model="state.form.date"
              type="date"
              placeholder="选择日期"
          />

修改
          <el-date-picker
              v-model="state.form.date"
              type="date"
              placeholder="选择日期"
              value-format="YYYY-MM-DD"
          />


下一个功能,修改数据

跟“增加数据”步骤相似

后端项目,修改SQL语句,打开UseMapper,新增下面的代码(一定不能写错变量的名字!!!)

@Update("update user set name = #{name}, date = #{date}, address = #{address} ,user_no = #{userNo} where id = #{id} ")
void update(User user);

然后打开UseController,新增代码如下

    @PutMapping("/update")
    public Result update(@RequestBody User user){  //函数名原来是save
        userMapper.update(user);
        return Result.success();
    }

相应地,前端网络请求也要进行相应修改,来到HomeView.vue

原来
const save = () => {
  console.log(state.form)
  request.post('/user/save',state.form).then(res=>{
    if (res.code === '200'){
      ElMessage.success("操作成功")
      // 关闭弹窗
      dialogFormVisible.value = false;
    }else {
      ElMessage.error(res.msg)
    }
  },err=>{
    console.log("发送post请求失败:",err)
  }
  )
}    

修改
const save = () => {
  console.log(state.form)
  request({
    url: state.form.id ? '/user/update':'/user/save',
    method: state.form.id ? 'PUT' :'POST',
    data: state.form
  }).then(res=>{
    if (res.code === '200'){
      ElMessage.success("操作成功")
      dialogFormVisible.value = false
    }else{
      ElMessage.error("操作失败", res.msg)
    }
  })
}

更新数据操作成功后,发现,虽然数据库的记录更新了,

但是前端页面还没有及时更新,所以调用load()

const save = () => {
  //省略代码
    if (res.code === '200'){
      ElMessage.success("操作成功")
      dialogFormVisible.value = false
      load() //调用查询方法,及时更新数据  
    }
  //省略代码
}

补充一个细节,希望新增的记录能够显示在最前面,所以,SQL语句查询的时候,增加一个倒序排序

order by id desc

原来
@Select("select * from user where name like concat('%',#{name},'%')  or date like concat('%',#{name},'%') or address like concat('%',#{name},'%') or user_no like concat('%',#{name},'%') limit #{start}, #{pageSize}") 
List<User> selectPage(@Param("name") String name,@Param("start") Integer start,@Param("pageSize") Integer pageSize); //三个参数,注意相应类型

修改
@Select("select * from user where name like concat('%',#{name},'%')  or date like concat('%',#{name},'%') or address like concat('%',#{name},'%') or user_no like concat('%',#{name},'%')  order by desc limit #{start}, #{pageSize}") 
List<User> selectPage(@Param("name") String name,@Param("start") Integer start,@Param("pageSize") Integer pageSize); //三个参数,注意相应类型

后端

UseMapper新增代码

    @Delete("delete from user where id = #{id}")
    void delete(Integer id);

UseController新增代码

    @DeleteMapping("/del")
    public Result delete(@RequestParam Integer id){
        userMapper.delete(id);
        return Result.success();
    }

前端HomeView.vue (多余的代码省略了)

原来
<el-button @click.prevent="remove(scope.$index)">删除</el-button>
// 删除数据
const remove = (index) => {
  // 从index的位置开始,删除一行
  state.tableData.splice(index, 1)
}


修改
<el-button @click.prevent="remove(scope.row.id)">删除</el-button>
// 删除数据
const remove = (id) => {
  request.delete('user/del?id='+id).then(res=>{
    if (res.code === '200'){
      ElMessage.success("操作成功")
      load() //调用查询方法,及时更新数据
    }else{
      ElMessage.error("操作失败", res.msg)
    }
  })
}

以上就是小白入门增删改查的基础操作了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1227086.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

公网访问全能知识库工具AFFINE,Notion的免费开源替代

文章目录 公网访问全能知识库工具AFFINE&#xff0c;Notion的免费开源替代品前言1. 使用Docker安装AFFINE2. 安装cpolar内网穿透工具3. 配置AFFINE公网访问地址4. 实现公网远程访问AFFINE 公网访问全能知识库工具AFFINE&#xff0c;Notion的免费开源替代品 前言 AFFiNE 是一个…

照片+制作照片书神器,效果太棒了!

随着数码相机的普及&#xff0c;越来越多的人喜欢用照片记录生活点滴。而制作一本精美的照片书&#xff0c;不仅可以保存珍贵的回忆&#xff0c;还能让照片更加美观。今天&#xff0c;就为大家推荐一款制作照片书神器&#xff0c;让您的回忆更加完美&#xff01; 一、产品介绍 …

三栏布局,中间自适应

方法一&#xff1a;两边使用float 中间使用margin 方法二&#xff1a;两边使用absolute 中间使用margin 方法三&#xff1a;flex 布局 方法四&#xff1a;grid 布局 方法一&#xff1a;相关HTML代码【两边使用float 中间使用margin】 <div class"container"…

element-plus 表格-合并单元格

利用表格:span-method"" 方法实现合并单元格 合并前 合并后 重点代码generateIndexGroups &#xff0c;找到合并的单元格的index号 代码实现如下 <template><h2>实现表格的合并</h2><div><!-- :span-method"arraySpanMethod&quo…

录制第一个jmeter性能测试脚本2(http协议)

我们手工编写了一个测试计划&#xff0c;现在我们通过录制的方式来实现那个测试计划。也就是说‘’测试计划目标和上一节类似&#xff1a;让5个用户在2s内登录webtour&#xff0c;然后进入 页面进行查看。 目录 一.性能测试脚本录制的原理 二、性能测试脚本录制的实操&#…

ZC序列理论学习及仿真

文章目录 前言一、ZC 序列理论1、基本概念2、表达式3、ZC 序列一些定义①、自相关②、循环移位③、循环自相关④、循环互相关二、ZC 序列性质1、性质 1:恒包络,即等模2、性质 2:零循环自相关3、性质 3:固定循环互相关4、其他性质①、傅里叶变换后仍是 ZC 序列②、低峰均比③…

C语言基础篇4:变量、存储、库函数

1 局部变量和全局变量 在介绍局部变量和全局变量前&#xff0c;先了解一些关于作用域方面的内容。作用域的作用就是决定程序中的哪些语句是可用的&#xff0c;换句话说&#xff0c;就是程序中的可见性。作用域有局部作用域和全局作用域&#xff0c;那么局部变量就具有局部作用域…

【C++】:模板的使用

目录 1、泛型编程 2、函数模板 2.1、函数模板概念 2.2、函数模板格式 2.3、函数模板的原理 2.4、函数模板的实例化 2.6、模板参数的匹配原则 3、类模板 3.1、 类模板的定义格式 3.2、 类模板的实例化 4、非类型模板参数 5、模板的特化 5.1、函数模板特化 5.2、类模…

2.如何使用Python官方文档

文章目录 1.文档什么时候用2.Python 3.11有什么新的功能3.Python 3.11教程4.Python 标准库概述内置函数内置类型内置异常文本处理服务 1.文档什么时候用 一般是在接触到一门新的语言的时候&#xff0c;是要详细阅读的&#xff0c;因为很多在开发的时候踩的这些坑在文档里已经提…

基于Prometheus快速搭建网络质量监控平台

基于Prometheus快速搭建网络质量监控平台 前言 针对一些临时搭建的网络场景&#xff08;例如&#xff1a;各种临时发布会&#xff0c;演唱会等&#xff09;&#xff0c;为了检验网络质量的以及确保管理员在网络出现异常情况时可以及时发现&#xff0c;需要部署一套监控软件用于…

a标签下载文件与解决浏览器默认打开某些格式文件的问题

前言 在实际项目中&#xff0c;我们通常会遇到这么一个需求&#xff1a;后端给前端返回一个任意文件类型的完整的url路径&#xff0c;前端拿到这个路径直接通过浏览器下载文件到本地。我想大家应该都会首先想到使用HTML中的<a>标签&#xff0c;&#xff0c;因为<a>…

国学---佛系算吉凶~

佛系算吉凶咯~&#xff0c;正经走访深山庙宇&#xff0c;前辈老人&#xff0c;经过调研后&#xff0c;搭建的轻衍计算模型&#xff0c;团队对国学的初次信息化尝试。 共享给有需要的朋友&#xff0c;准不准没关系&#xff0c;开心最重要。 后续还有财富&#xff0c;事业&…

【Python】解析CPP类定义代码,获取UML类图信息

参考 & 鸣谢 CppHeaderParser - 官方文档Python解析C头文件win10直接获得文件绝对路径的方法总结 目的 解析CPP头文件中的类定义&#xff0c;获取UML中的属性。用于画UML类图。如下所示格式&#xff0c;图片来源-链接 即获取&#xff0c;类名&#xff0c;成员函数&#x…

05_SHELL编程之文本处理工具SED

typora-root-url: pictures课程目标 掌握sed的基本语法结构 熟悉sed常用的命令&#xff0c;如打印p&#xff0c;删除d&#xff0c;插入i等 Windows&#xff1a;​ Linux&#xff1a; vim vi gedit nano emacs 一、sed介绍 1. sed的工作流程 首先sed把当前正在处理的行保存…

python urllib open 头部信息错误

header 有些字符在 lighttpd server 中无法正常解析,需要转换 quteo 可以转换 就跨平台而言,Rust 和 python 一样优秀,看了在stm32 上使用 Rust 进行编程,从一定程度上,而言&#xff0c;稳定和安全性要比C 开发的好的多,说出来可能不信&#xff0c;在单片机上是可以对空指针进行…

力扣周赛372 模拟 思维 位运算 java

100131. 使三个字符串相等 ⭐ AC code class Solution {public int findMinimumOperations(String s1, String s2, String s3) {int len1 s1.length();int len2 s2.length();int len3 s3.length();int n Math.min(len1,len2);n Math.min(n,len3);int i 0;while(i < n…

代码随想录二刷 | 数组 | 移除元素

代码随想录二刷 &#xff5c; 数组 &#xff5c; 移除元素 题目描述解题思路 & 代码实现暴力解法双指针法 题目描述 27. 移除元素 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。 不要使用…

Ubuntu(Linux)的基本操作

基本操作三步走 1、输入vim code.c点击i&#xff08;出现insert&#xff09;表示可以编辑代码编辑代码之后按下esc&#xff08;退出编辑模式&#xff09;按下shift:&#xff08;冒号&#xff09;wq&#xff08;退出文件&#xff09;2、输入gcc code.c&#xff08;进行编译代码…

RobotFramework框架之导入自己打包的python程序(十五)

引言 RobotFramework自动化框架&#xff08;以下简称RF&#xff09;之前文章我们讲了通过import第三方的library&#xff08;RequestsLibrary等&#xff09;&#xff0c;在实际项目中第三方的包并不能满足我们的需要&#xff0c;此时我们可自己编写python模块&#xff08;.py文…

命令执行相关函数及各类命令执行绕过技巧

相关函数 &#xff08;命令注入&#xff09; 命令执行的绕过