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

项目笔记,教学视频来源于B站青戈

https://www.bilibili.com/video/BV1H14y1S7YV

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

查 +分页

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

前端分页组件

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

html 复制代码
<template>
  <el-pagination background layout="prev, pager, next" :total="1000" />
</template>

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

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

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


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

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

后端SQL语句修改

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

sql 复制代码
select * from user where name like '%张%' or date like '%张%' or address like '%张%' or user_no like '%张%' LIMIT 1,2

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

在UserMapper新增SQL代码;

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

java 复制代码
@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这个文件添加以下代码

java 复制代码
    @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语句还参数一致的

js 复制代码
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前端分页组件的代码,加上两个数据绑定

html 复制代码
原来
<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加上这两个变量,为了把页码的两个参数变成动态的,初步修改如下

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,故手动修改。

js 复制代码
const total = ref(2) //总记录数

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

html 复制代码
原来(其他属性省略没写)
 <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 ,添加下面的代码

js 复制代码
import zhCn from 'element-plus/dist/locale/zh-cn.min'

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

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

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

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

动态total 修改

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

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

java 复制代码
@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这个变量,把数值顺利传递到前端,修改如下

java 复制代码
原来
@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修改

js 复制代码
原来
<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找到日期选择器组件,复制粘贴然后根据自身需求修改代码,如下

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

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

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

来到UseController,新增代码如下

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

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

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

js 复制代码
原来
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)  //现在是后端返回错误
  }
  )
}

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

cmd 复制代码
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添加三个注解,并且引入相关的包

java 复制代码
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor

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

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

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

html 复制代码
原来
          <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,新增下面的代码(一定不能写错变量的名字!!!)

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

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

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

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

js 复制代码
原来
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()

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

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

order by id desc

java 复制代码
原来
@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新增代码

java 复制代码
    @Delete("delete from user where id = #{id}")
    void delete(Integer id);

UseController新增代码

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

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

js 复制代码
原来
<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)
    }
  })
}

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

相关推荐
dkmilk6 分钟前
Tomcat发布websocket
java·websocket·tomcat
工一木子27 分钟前
【Java项目脚手架系列】第七篇:Spring Boot + Redis项目脚手架
java·spring boot·redis
哞哞不熬夜39 分钟前
JavaEE--初识网络
java·网络·java-ee
noravinsc1 小时前
redis是内存级缓存吗
后端·python·django
等等5431 小时前
Java EE初阶——wait 和 notify
java·开发语言
API小爬虫1 小时前
淘宝按图搜索商品(拍立淘)Java 爬虫实战指南
java·爬虫·图搜索算法
lyrhhhhhhhh1 小时前
Spring 框架 JDBC 模板技术详解
java·数据库·spring
满怀10152 小时前
【Vue 3全栈实战】从响应式原理到企业级架构设计
前端·javascript·vue.js·vue
亚林瓜子2 小时前
AWS Elastic Beanstalk控制台部署Spring极简工程
java·spring·云计算·aws·eb
noravinsc2 小时前
django中用 InforSuite RDS 替代memcache
后端·python·django