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
属性有多个属性值,它们有不同的功能,从左到右依次为
- total,表示后端数据的总条数,即页面上的 “ 共400条 ”
- sizes,表示每页能容纳多少条数据,即页面上的下拉选框 “ 100条/页 ”
- prev,表示向前翻一页,即页面上的 “ < ”
- pager,表示指定翻到哪一页,即页面上的 “ 1 2 3 4 ”
- next,表示向后翻一页,即页面上的 “ > ”
- jumper,表示直接跳去哪一页,即页面上的 “ 前往 3 页 ”
二个核心事件
- @size-change事件,当你使用下拉选框改变page-size属性时,它能监听到page-size属性的变化,并立刻将最新的值自动传给的事件处理函数,你甚至无需给它手动传参
- @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)
}
})
}
以上就是小白入门增删改查的基础操作了