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

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)
    }
  })
}

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


http://www.kler.cn/a/134358.html

相关文章:

  • Scala学习记录,case class,迭代器
  • C++《继承》
  • acwing算法基础02一高精度,前缀和,差分
  • 06.VSCODE:备战大项目,CMake专项配置
  • 密码学的基本原理
  • 【学习】Fine-tuning知识汇总
  • 目标检测YOLO实战应用案例100讲-基于机器视觉的水稻病虫害监测预警(续)
  • Git命令总结-常用-后续使用频繁的再添加~
  • RSocket 与 gRPC 基准测试
  • 【开题报告】疫苗在线预约小程序的设计与实现
  • python数据可视化
  • WPF中行为与触发器的概念及用法
  • 2023年【广东省安全员C证第四批(专职安全生产管理人员)】考试题库及广东省安全员C证第四批(专职安全生产管理人员)考试试卷
  • 系统时间和JVM的Date时间不一致问题解决
  • .Net6 部署到IIS示例
  • 【电路笔记】-欧姆定律
  • 100套Axure RP大数据可视化大屏模板及通用组件库
  • Linux环境下C++ 接入OpenSSL
  • 无需添加udid,ios企业证书的自助生成方法
  • 系列九、对象的生命周期和GC
  • Linux虚拟机中网络连接的三种方式
  • MySQL 教程 1.1
  • “ /^A-Z:\\{1,2}^/:\*\?<>\|+\.(jpg|gif|png|bmp)$/i ”这个正则表达式的理解
  • 月子会所信息展示服务预约小程序的作用是什么
  • Git-概念与架构
  • C语言 字符函数汇总,模拟实现各字符函数(炒鸡详细)