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

项目笔记,教学视频来源于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)
    }
  })
}

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

在Vue关于ue的computed属性中传递参数

computed的基本用法 computed是Vue实例中一个非常强大的计算属性&#xff0c;它的值主要根据依赖数据而变化。我们可以将一些简单的计算放在computed属性中&#xff0c;从而实现在模板中使用简单的表达式。 但是实际项目中通常有这么一个场景&#xff1a;根据传递不一样的参数值…

SQL零基础入门教程,贼拉详细!贼拉简单! 速通数据库期末考!(九)

UNION ALL UNION ALL 用于合并两个或多个 SELECT 语句的结果。 请注意&#xff0c;UNION ALL 合并的每个 SELECT 语句必须是查询相同数量&#xff0c;相同数据类型的字段&#xff0c;且顺序也必须一致。另外结果集中的列名总是等于 UNION ALL 中第一个 SELECT 语句中的列名。 …

Frames X for figma 组件库设计系统 Local Variables下载

简而言之&#xff0c;Frames X 是最出色、易于使用且文档齐全的Figma 设计系统之一。 它包括经过深思熟虑的设计指南和现成的组件&#xff0c;并且还提供一本全面的电子书&#xff0c;其中将详细解释如何使用该套件中包含的所有内容。 事实上&#xff0c;电子书使Frames X 与…

【python】--python基础学习

目录 一、基础语法二、基础数据类型1、变量赋值2、数值型3、字符串型4、列表List5、元组Tuple6、字典dictionary7、数据类型转换 三、python运算符四、条件控制与循环五、常用函数1、字符串函数2、format函数 一、基础语法 标识符是允许作为变量&#xff08;函数、类等&#x…

HDFS、MapReduce原理--学习笔记

1.Hadoop框架 1.1框架与Hadoop架构简介 &#xff08;1&#xff09;广义解释 从广义上来说&#xff0c;随着大数据开发技术的快速发展与逐步成熟&#xff0c;在行业里&#xff0c;Hadoop可以泛指为&#xff1a;Hadoop生态圈。 也就是说&#xff0c;Hadoop指的是大数据生态圈整…

python urllib open 头部信息错误

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

02 elementplus前端增删改查【小白入门SpringBoot+Vue3】

视频教程来源于 B站青戈 https://www.bilibili.com/video/BV1H14y1S7YV 只用elementplus&#xff0c;学点增删改查&#xff0c;还没有于后端连接起来&#xff0c;具体在下一篇 搭建一个小页面&#xff0c;显示数据 补充&#xff1a;webstorm格式化代码&#xff0c;修改了快捷…

java+mysql的校园兼职微信小程序(附源码 调试 文档)

校园兼职微信小程序 摘要一、引言二、国内外研究现状三、系统设计四、系统实现与界面展示五、源码获取 摘要 本文详述了一个基于Java和MySQL数据库技术的校园兼职微信小程序的毕业设计。系统主要分为三种用户角色&#xff1a;管理员、学生用户和商家用户。管理员拥有学生管理、…

Adversarially Robust Neural Architecture Search for Graph Neural Networks

Adversarially Robust Neural Architecture Search for Graph Neural Networks----《面向图神经网络的对抗鲁棒神经架构搜索》 摘要 图神经网络&#xff08;GNN&#xff09;在关系数据建模方面取得了巨大成功。尽管如此&#xff0c;它们仍然容易受到对抗性攻击&#xff0c;这对…

思维模型 留白效应

本系列文章 主要是 分享 思维模型 &#xff0c;涉及各个领域&#xff0c;重在提升认知。因留白而遐想。 1 留白效应的应用 1.1 留白效应在艺术领域的应用 欧洲的艺术和设计领域有很多经典的实际案例&#xff0c;其中荷兰画家文森特梵高的作品《星夜》是一幅非常著名的油画&am…

合并两个有序链表(冒泡排序实现)

实例要求&#xff1a;将两个升序链表合并为一个新的 升序 链表并返回&#xff1b;新链表是通过拼接给定的两个链表的所有节点组成的&#xff1b;实例分析&#xff1a;先拼接两个链表&#xff0c;在使用冒泡排序即可&#xff1b;示例代码&#xff1a; struct ListNode* mergeTwo…

EMD、EEMD、FEEMD、CEEMDAN分解的对比(其中CEEMDAN分解可以有效消除模态分解)

理论部分 EMD (Empirical Mode Decomposition)、EEMD (Ensemble EMD)、FEEMD (Fast Ensemble EMD) 和 CEEMDAN (Complete Ensemble EMD with Adaptive Noise) 是一些常用的信号分解方法&#xff0c;它们在信号分解的效果和特性上有所区别。 1. EMD&#xff1a; - EMD是最基础…

2023年【G1工业锅炉司炉】报名考试及G1工业锅炉司炉理论考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 G1工业锅炉司炉报名考试是安全生产模拟考试一点通生成的&#xff0c;G1工业锅炉司炉证模拟考试题库是根据G1工业锅炉司炉最新版教材汇编出G1工业锅炉司炉仿真模拟考试。2023年【G1工业锅炉司炉】报名考试及G1工业锅炉…

设计模式-行为型模式-策略模式

一、什么是策略模式 策略模式是一种行为设计模式&#xff0c;它允许在运行时选择算法或行为&#xff0c;并将其封装成独立的对象&#xff0c;使得这些算法或行为可以相互替换&#xff0c;而不影响使用它们的客户端。&#xff08;ChatGPT生成&#xff09; 主要组成部分&#xff…

数据结构与算法-图

图 &#x1f388;2.图的存储结构&#x1f4d6;2.4.2邻接表的存储✅2.4.2.1逆邻接表✅2.4.2.2邻接表存储结构的定义✅2.4.2.3邻接表存储结构的类定义✅2.4.2.4创建n个顶点m条边的无向网✅2.4.2.5创建n个顶点m条边的有向网✅2.4.2.6定位操作-查找定点信息在顶点数组中的下标✅2.4…

Spring Boot中使用Redis进行大数据缓存

Spring Boot中使用Redis进行大数据缓存 在Spring Boot中使用Redis进行大数据缓存是一种常见的做法&#xff0c;因为Redis是一种高性能的内存数据库&#xff0c;适用于缓存大量数据。以下是说明和示例代码&#xff0c;演示如何在Spring Boot项目中使用Redis进行大数据缓存。 步…

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

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

3DMAX森林树木植物插件ForestPackLite教程

3DMAX森林树木植物插件ForestPackLite教程 Forest Pack是世界上最受欢迎的散布插件。它提供了一个完整的解决方案来创建大面积的物体&#xff0c;从树木和植物到建筑、人群、骨料、地面覆盖物、岩石等等。如果你能为它建模&#xff0c;森林包就能把它分散开来。 无数工作室依靠…

安装Nginx时报:./configure: error: can not define uint32_t

安装版本&#xff1a;v.1.25.3 安装命令&#xff1a; ./configure --prefix/opt/nginx/1.25.3 && make && make install 执行命令后出现异常&#xff0c;如图&#xff1a; 解决方法&#xff1a; yum install -y kernel-headers kernel-devel gcc make

算数通关村第十一关-白银挑战位运算高频题

位移的运算 位1的个数 描述 : 编写一个函数&#xff0c;输入是一个无符号整数&#xff08;以二进制串的形式&#xff09;&#xff0c;返回其二进制表达式中数字位数为 1 的个数&#xff08;也被称为汉明重量&#xff09;。 题目 : LeetCode 191.位1的个数 191. 位1的个数…