springboot+VUE+部署(13。创建多表查询)

基于现有的告警列表查询接口,在返回 x_alarm 表所有数据的同时,根据 devid 关联 x_devicename 表,补充返回设备名称和设备安装地址这两个字段,并且保留原有的分页和条件查询功能。

实现思路

  1. 数据关联 :通过 devidx_alarmx_devicename 表关联,查询时补充设备名称、地址字段;
  2. DTO 封装:创建一个包含告警信息 + 设备信息的 DTO 类,用于接收关联查询结果;
  3. 修改查询逻辑:替换原有的单表分页查询,改为自定义 SQL 关联查询,保留分页和条件过滤;
  4. 结果封装:将查询结果封装成前端需要的格式(总数 + 分页数据)。

完整实现代码

步骤 1:创建告警扩展 DTO 类(包含设备信息)
复制代码
import lombok.Data;
import java.util.Date;

/**
 * 告警信息扩展DTO(包含设备名称和地址)
 */
@Data
public class AlarmExtDTO {
    // 告警表原有字段
    private Integer uid;
    private String devid;
    private String alarmtype;
    private Date alarmtime;
    private String operation;
    private Date operationtime;
    private String operusername;
    
    // 关联设备表的字段
    private String devname;      // 设备名称
    private String devaddress;   // 设备安装地址
}

步骤 2:修改 Mapper 层(自定义关联分页查询)

复制代码
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

public interface AlarmMapper {

    /**
     * 关联查询告警+设备信息(带分页和条件)
     */
    @Select("SELECT " +
            "a.uid, a.devid, a.alarmtype, a.alarmtime, a.operation, " +
            "a.operationtime, a.operusername, " +
            "b.devname, b.devaddress " +
            "FROM x_alarm a " +
            "LEFT JOIN x_devicename b ON a.devid = b.devid " + // 左连接:保证无设备信息的告警也能查询到
            "${ew.customSqlSegment}") // 用于拼接MyBatis-Plus的条件
    IPage<AlarmExtDTO> selectAlarmWithDevice(Page<AlarmExtDTO> page, 
                                            @Param(Constants.WRAPPER) Wrapper<Alarm> wrapper);
}

步骤 3:修改 Service 层(可选,封装业务逻辑)

复制代码
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

@Service
public class AlarmServiceImpl extends ServiceImpl<AlarmMapper, Alarm> implements AlarmService {

    @Override
    public Page<AlarmExtDTO> getAlarmWithDevicePage(Page<AlarmExtDTO> page, 
                                                  String devid, String alarmtype) {
        LambdaQueryWrapper<Alarm> wrapper = new LambdaQueryWrapper<>();
        // 保留原有条件过滤
        wrapper.like(StringUtils.hasLength(devid), Alarm::getDevid, devid);
        wrapper.like(StringUtils.hasLength(alarmtype), Alarm::getAlarmtype, alarmtype);
        // 执行关联分页查询
        return baseMapper.selectAlarmWithDevice(page, wrapper);
    }
}

// 对应的Service接口
public interface AlarmService extends IService<Alarm> {
    Page<AlarmExtDTO> getAlarmWithDevicePage(Page<AlarmExtDTO> page, String devid, String alarmtype);
}

步骤 4:修改 Controller 层(最终接口)

复制代码
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.util.HashMap;

@RestController
public class AlarmController {

    private final AlarmService alarmService;

    // 构造器注入
    public AlarmController(AlarmService alarmService) {
        this.alarmService = alarmService;
    }

    @GetMapping("/alarm/list")
    public Result<Map<String, Object>> getalarmList(
            @RequestParam(value = "devid", required = false) String devid,
            @RequestParam(value = "alarmtype", required = false) String alarmtype,
            @RequestParam(value = "pageNo") Long pageNo,
            @RequestParam(value = "pageSize") Long pageSize) {

        // 构建分页对象
        Page<AlarmExtDTO> page = new Page<>(pageNo, pageSize);
        // 调用关联查询方法
        Page<AlarmExtDTO> resultPage = alarmService.getAlarmWithDevicePage(page, devid, alarmtype);

        // 封装返回结果(保持原有格式)
        HashMap<String, Object> data = new HashMap<>();
        data.put("total", resultPage.getTotal()); // 总条数
        data.put("rows", resultPage.getRecords()); // 分页数据(包含设备名称/地址)
        return Result.success(data);
    }
}

关键说明

  1. 表关联方式 :使用 LEFT JOIN(左连接)而非内连接,确保即使 x_devicename 中没有对应 devid 的记录,告警数据也能正常返回(设备名称 / 地址为 null);
  2. 条件复用 :通过 ${ew.customSqlSegment} 复用 MyBatis-Plus 的 LambdaQueryWrapper 条件,无需手动拼接 where 语句,保留原有的 devidalarmtype 模糊查询;
  3. DTO 设计 :新增 AlarmExtDTO 专门用于接收关联查询结果,避免修改原有 Alarm 实体类,符合 "单一职责" 原则;
  4. 分页兼容:完全保留原有的分页参数(pageNo/pageSize)和返回格式(total/rows),前端无需修改。

测试示例

假设数据库中有以下数据:

  • x_alarm:devid = "DEV001",alarmtype = "故障告警"
  • x_devicename:devid = "DEV001",devname = "温湿度传感器",devaddress = "一号厂房 3 楼"

调用接口:/alarm/list?devid=DEV001&pageNo=1&pageSize=10

总结

  1. 核心改动是通过 LEFT JOIN 关联两张表,并新增 DTO 接收扩展字段;
  2. 复用 MyBatis-Plus 的条件构造器和分页插件,保留原有查询条件和分页逻辑;
  3. 接口返回格式不变,仅在 rows 数组中新增 devnamedevaddress 字段,前端可直接使用。

创建位置规范(遵循 Java 项目最佳实践)

在 Spring Boot/Spring MVC 项目中,DTO(数据传输对象)类的创建位置有固定的规范,通常遵循以下目录结构:

plaintext

复制代码
src/main/java/
└── com/你的项目包名/       # 根包(比如 com.company.project)
    ├── controller/        # 控制器(已有)
    ├── service/           # 服务层(已有)
    ├── mapper/            # Mapper层(已有)
    ├── entity/            # 数据库实体类(对应x_alarm、x_devicename的实体,已有)
    └── dto/               # 新增:专门存放DTO类的目录
        └── alarm/         # 可选:按业务模块细分(比如告警模块)
            └── AlarmExtDTO.java  # 你的告警扩展DTO类

二、IDEA 中具体创建步骤

步骤 1:定位并创建 dto 目录
  1. 在 IDEA 的「Project」面板中,展开 src/main/java → 展开你的项目根包(比如 com.xxx.project);
  2. 右键点击根包 → 选择 NewPackage
  3. 输入包名 dto(如果想按模块细分,可输入 dto.alarm)→ 回车确认。
步骤 2:创建 AlarmExtDTO 类
  1. 右键点击刚创建的 dto(或 dto.alarm)包 → 选择 NewJava Class
  2. 在弹出的输入框中输入类名 AlarmExtDTO → 回车;
  3. 把之前提供的 DTO 代码复制到这个类文件中,补充对应的包名(IDEA 会自动提示补全)。
相关推荐
今心上3 小时前
spring中的@Autowired到底是什么
java·后端·spring
心.c3 小时前
虚拟滚动列表
前端·javascript·vue.js·js
无心水3 小时前
【任务调度:数据库锁 + 线程池实战】4、架构实战:用线程池 + SKIP LOCKED 构建高可用分布式调度引擎
人工智能·分布式·后端·spring·架构
Coder_Boy_3 小时前
Java高级_资深_架构岗 核心知识点(模块三:高并发)
java·spring boot·分布式·面试·架构
Coder_Boy_4 小时前
Java高级_资深_架构岗 核心知识点全解析(模块二:Spring生态 架构岗必备)
java·spring boot·spring·架构
bugcome_com4 小时前
【C# 数组详解】Array 定义、初始化、遍历、内存原理与面试高频问题
后端·c#·asp.net
暮色妖娆丶4 小时前
Spring 源码分析 Lifecycle Bean
spring boot·spring·源码
艾醒4 小时前
打破信息差——2月21日AI全域热点全复盘
后端·算法
倚肆4 小时前
WebSocket连接教程示例(Spring Boot + STOMP + SockJS + Vue)
vue.js·spring boot·websocket