目录
- 前言
- 一、与页面原型差距
- 1.现在:
- 2.目标:
- 3. 存在问题:所在区域和合作商ID展示的都是ID,而不是名称;同时合作商ID应改为合作商
- 二、修改
- 1.重新设计SQL语句
- 2.修改mapper层,使用Mybatis中的嵌套查询
- 3.修改service层
- 4. 修改controller层
- 5.前端修改
- 6.补充区域查看详情
- 7.数据完整性
前言
提示:本篇目的是将点位管理中所在区域和合作商展示的ID改为对应的名称
一、与页面原型差距
1.现在:
点位管理的字段
{
"total": 3,
"rows": [
{
"createBy": null,
"createTime": "2024-07-03 10:26:05",
"updateBy": null,
"updateTime": "2024-07-03 10:26:05",
"remark": null,
"id": 1,
"nodeName": "三里屯点位",
"address": "北京市朝阳区三里屯路",
"businessType": 1,
"regionId": 1,
"partnerId": 1
},
...
],
"code": 200,
"msg": "查询成功"
}
2.目标:
要求返回类型
3. 存在问题:所在区域和合作商ID展示的都是ID,而不是名称;同时合作商ID应改为合作商
现阶段后端返回字段和目标字段对比,发现缺少region和partner信息
二、修改
1.重新设计SQL语句
-- 查询并显示点位表所有的字段信息,同时显示每个点位的设备数量
SELECT
n.id,
n.node_name,
n.address,
n.business_type,
n.region_id,
n.partner_id,
n.create_time,
n.update_time,
n.create_by,
n.update_by,
n.remark,
COUNT(v.id) AS vm_count
FROM
tb_node n
LEFT JOIN
tb_vending_machine v ON n.id = v.node_id
GROUP BY
n.id;
-- 根据区域id查询区域信息
select * from tb_region where id=1;
-- 根据合作商id查询合作商信息
select * from tb_partner where id=1;
2.修改mapper层,使用Mybatis中的嵌套查询
NodeVo
@Data
public class NodeVo extends Node {
// 设备数量
private int vmCount;
// 区域
private Region region;
// 合作商
private Partner partner;
}
NodeMapper.java
/**
* 查询点位管理列表
* @param node
* @return NodeVo集合
*/
public List<NodeVo> selectNodeVoList(Node node);
NodeMapper.xml
<resultMap type="NodeVo" id="NodeVoResult">
<result property="id" column="id" />
<result property="nodeName" column="node_name" />
<result property="address" column="address" />
<result property="businessType" column="business_type" />
<result property="regionId" column="region_id" />
<result property="partnerId" column="partner_id" />
<result property="createTime" column="create_time" />
<result property="updateTime" column="update_time" />
<result property="createBy" column="create_by" />
<result property="updateBy" column="update_by" />
<result property="remark" column="remark" />
<result property="vmCount" column="vm_count" />
<association property="region" javaType="Region" column="region_id" select="com.dkd.manage.mapper.RegionMapper.selectRegionById"/>
<association property="partner" javaType="Partner" column="partner_id" select="com.dkd.manage.mapper.PartnerMapper.selectPartnerById"/>
</resultMap>
<select id="selectNodeVoList" resultMap="NodeVoResult">
SELECT
n.id,
n.node_name,
n.address,
n.business_type,
n.region_id,
n.partner_id,
n.create_time,
n.update_time,
n.create_by,
n.update_by,
n.remark,
COUNT(v.id) AS vm_count
FROM
tb_node n
LEFT JOIN
tb_vending_machine v ON n.id = v.node_id
<where>
<if test="nodeName != null and nodeName != ''"> and n.node_name like concat('%', #{nodeName}, '%')</if>
<if test="regionId != null "> and n.region_id = #{regionId}</if>
<if test="partnerId != null "> and n.partner_id = #{partnerId}</if>
</where>
GROUP BY
n.id
</select>
3.修改service层
NodeService
/**
* 查询点位管理列表
* @param node
* @return NodeVo集合
*/
public List<NodeVo> selectNodeVoList(Node node);
NodeServiceImpl
/**
* 查询点位管理列表
*
* @param node
* @return NodeVo集合
*/
@Override
public List<NodeVo> selectNodeVoList(Node node) {
return nodeMapper.selectNodeVoList(node);
}
4. 修改controller层
NodeController
/**
* 查询点位管理列表
*/
@PreAuthorize("@ss.hasPermi('manage:node:list')")
@GetMapping("/list")
public TableDataInfo list(Node node)
{
startPage();
List<NodeVo> voList = nodeService.selectNodeVoList(node);
return getDataTable(voList);
}
5.前端修改
node/index.vue
<!-- 点位列表 -->
<el-table v-loading="loading" :data="nodeList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" type="index" width="50" align="center" prop="id" />
<el-table-column label="点位名称" align="center" prop="nodeName" />
<el-table-column label="所在区域" align="center" prop="region.regionName" />
<el-table-column label="商圈类型" align="center" prop="businessType">
<template #default="scope">
<dict-tag :options="business_type" :value="scope.row.businessType" />
</template>
</el-table-column>
<el-table-column label="合作商" align="center" prop="partner.partnerName" />
<el-table-column label="详细地址" align="center" prop="address" show-overflow-tooltip="true"/>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['manage:node:edit']">修改</el-button>
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['manage:node:remove']">删除</el-button>
</template>
</el-table-column>
</el-table>
6.补充区域查看详情
在region/index.vue视图组件中修改
<el-button link type="primary" @click="getRegionInfo(scope.row)" v-hasPermi="['manage:node:list']">查看详情</el-button>
<!-- 查看详情对话框 -->
<el-dialog title="区域详情" v-model="regionInfoOpen" width="500px" append-to-body>
<el-form-item label="区域名称" prop="regionName">
<el-input v-model="form.regionName" disabled />
</el-form-item>
<label>包含点位:</label>
<el-table :data="nodeList">
<el-table-column label="序号" type="index" width="50" align="center" />
<el-table-column label="点位名称" align="center" prop="nodeName" />
<el-table-column label="设备数量" align="center" prop="vmCount" />
</el-table>
</el-dialog>
<script>
import { listNode } from "@/api/manage/node";
import { loadAllParams } from "@/api/page";
/* 查看详情按钮操作 */
const nodeList = ref([]);
const regionInfoOpen = ref(false);
function getRegionInfo(row) {
// 查询区域信息
reset();
const _id = row.id
getRegion(_id).then(response => {
form.value = response.data;
});
// 查询点位列表
loadAllParams.regionId = row.id;
listNode(loadAllParams).then(response => {
nodeList.value = response.rows;
});
regionInfoOpen.value = true;
</script>
7.数据完整性
现在我们要思考一个问题,当我们删除区域或合作商数据时,与之关联的点位数据该如何处理?
在默认情况下,由于我们在创建点位表时通过AI设置了外键约束,并配置了级联删除操作,所以删除区域或合作商会导致其关联的点位数据一并被删除。从技术角度来看,这是符合数据库的外键约束规则的。
但是,从业务角度来看,这种做法可能不太合适。想象一下,如果一个区域下有多个点位,一次误操作就可能导致所有的点位数据及其关联的设备信息被一并删除,这显然是我们不愿意看到的。
因此,我们需要对级联操作进行修改,将其改为限制删除。这样,当尝试删除一个区域或合作商时,如果它下面还有关联的点位数据,数据库将不会允许删除操作,并会给出错误提示。
使用Navicat修改tb_node表:
CASCADE(级联操作):当父表中的某行记录被删除或更新时,与其关联的所有子表中的匹配行也会自动被删除或更新。这种方式适用于希望保持数据一致性的场景,即父记录不存在时,相关的子记录也应该被移除。
SET NULL(设为空):若父表中的记录被删除或更新,子表中对应的外键字段会被设置为NULL。选择此选项的前提是子表的外键列允许为NULL值。这适用于那些子记录不再需要明确关联到任何父记录的情况。
RESTRICT(限制):在尝试删除或更新父表中的记录之前,数据库首先检查是否有相关联的子记录存在。如果有,则拒绝执行删除或更新操作,以防止意外丢失数据或破坏数据关系的完整性。这是一种保守策略,确保数据间的引用完整性。
NO ACTION(无操作):在标准SQL中,NO ACTION是一个关键字,它要求数据库在父表记录被删除或更新前,检查是否会影响子表中的相关记录。在MySQL中,NO ACTION的行为与RESTRICT相同,即如果子表中有匹配的行,则禁止执行父表的删除或更新操作。这意味着如果存在依赖关系,操作将被阻止,从而保护数据的参照完整性。
修改完毕后,如果你尝试进行删除操作,会发现数据库的完整性约束生效了,它会阻止删除操作并给出错误提示。但是,这个错误提示信息可能对于用户来说不够友好,可能会让用户感到困惑。
SQLIntegrityConstraintViolationException是Java中的一个异常类,这个类通常用于表示SQL数据库操作中的完整性约束违反异常
例如:外键约束、唯一约束等。当数据库操作违反了这些约束时,就会抛出这个异常。
这个错误是由于外键约束导致的。它表明在删除或更新父表的行时,存在外键约束,子表中的相关行会受到影响。
是因为在删除tb_region表中的行时,tb_node表中的region_id外键约束会阻止操作。
如果你在使用Spring框架进行数据库操作,可能会先遇到DataIntegrityViolationException,它是对SQLIntegrityConstraintViolationException的一个更高层次的抽象,旨在提供一种更加面向应用的错误表示。
而SQLIntegrityConstraintViolationException是更底层的异常,直接来源于数据库驱动,包含更多底层数据库相关的细节。
在实际开发中,推荐捕获并处理DataIntegrityViolationException,因为它更符合Spring应用的异常处理模式,同时也可以通过其内部的cause(原因)属性来获取具体的SQLIntegrityConstraintViolationException,进而获取详细的错误信息。
为了提升用户体验,我们可以使用Spring Boot框架的全局异常处理器来捕获这些错误信息,并返回更友好的提示信息给用户。这样,当用户遇到这种情况时,他们将收到一个清晰、易懂的提示,告知他们操作无法完成的原因。
修改全局异常处理器,添加以下内容
/**
* 数据完整性异常
*/
@ExceptionHandler(DataIntegrityViolationException.class)
public AjaxResult handelDataIntegrityViolationException(DataIntegrityViolationException e) {
if (e.getMessage().contains("foreign")) {
return AjaxResult.error("无法删除,有其他数据引用");
}
return AjaxResult.error("您的操作违反了数据库中的完整性约束");
}