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

在Spring Boot + MyBatis中优雅处理多表数据清洗:基于XML的配置化方案

问题背景

在实际业务中,我们常会遇到数据冗余问题。例如,一个公司表(sys_company)中存在多条相同公司名的记录,但只有一条有效(del_flag=0),其余需要删除。删除前需将关联表(如合同草稿表、发票表等)的外键字段(如purchaser_id)替换为保留记录的ID。这类问题通常涉及多表、多字段的动态更新,如何高效且安全地实现?


解决方案

我们将通过以下步骤实现:

  1. 配置化驱动:用配置类声明需要处理的表和字段,避免硬编码。
  2. 动态SQL更新:通过MyBatis XML实现批量更新和删除。
  3. 事务一致性:确保所有操作原子化执行。

实现步骤

1. 定义实体类

CompanyRetainedInfo:封装需保留的公司信息

import lombok.Data;@Data
public class CompanyRetainedInfo {private String companyName;   // 公司名称private Long retainedId;      // 需保留的公司ID(del_flag=0的记录)private String retainedName;  // 需保留的公司名称(与companyName一致)
}

作用:映射查询结果,传递保留记录的ID和名称。
Lombok@Data 自动生成Getter/Setter和toString()方法。


2. 定义配置类

TableConfig:声明需处理的表和外键关系

public class TableConfig {private String tableName;    // 表名(如contract_draft)private String idColumn;     // 外键ID字段(如purchaser_id)private String nameColumn;  // 名称字段(如purchaser_name,可能为null)// 构造器 + Getter/Setterpublic TableConfig(String tableName, String idColumn, String nameColumn) {this.tableName = tableName;this.idColumn = idColumn;this.nameColumn = nameColumn;}
}

3. 编写MyBatis Mapper接口

CompanyCleanMapper:定义数据操作接口(无注解,纯XML映射

@Mapper
public interface CompanyCleanMapper {// 查询需保留的公司信息(del_flag=0)List<CompanyRetainedInfo> selectRetainedCompanies();// 根据公司名查询待删除的ID列表(del_flag!=0)List<Long> selectIdsToDelete(String companyName);// 更新关联表的外键引用void updateForeignKeys(@Param("config") TableConfig config,@Param("retainedId") Long retainedId,@Param("retainedName") String retainedName,@Param("ids") List<Long> ids);// 删除冗余公司记录void deleteCompanies(@Param("ids") List<Long> ids);
}

4. 实现XML映射文件

CompanyCleanMapper.xml:定义动态SQL逻辑

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.CompanyCleanMapper"><!-- 查询需保留的公司 --><select id="selectRetainedCompanies" resultType="CompanyRetainedInfo">SELECT company_name AS companyName, id AS retainedId, company_name AS retainedNameFROM sys_companyWHERE del_flag = 0AND company_name IN (SELECT company_nameFROM sys_companyGROUP BY company_nameHAVING COUNT(*) > 1 AND SUM(del_flag = 0) = 1)</select><!-- 查询待删除的ID列表 --><select id="selectIdsToDelete" resultType="long">SELECT idFROM sys_companyWHERE company_name = #{companyName}AND del_flag != 0</select><!-- 动态更新外键引用 --><update id="updateForeignKeys">UPDATE ${config.tableName}SET<choose><when test="config.nameColumn != null"><!-- 同时更新ID和名称字段 -->${config.idColumn} = #{retainedId},${config.nameColumn} = #{retainedName}</when><otherwise><!-- 仅更新ID字段 -->${config.idColumn} = #{retainedId}</otherwise></choose>WHERE${config.idColumn} IN<foreach item="id" collection="ids" open="(" separator="," close=")">#{id}</foreach></update><!-- 批量删除公司记录 --><delete id="deleteCompanies">DELETE FROM sys_companyWHERE id IN<foreach item="id" collection="ids" open="(" separator="," close=")">#{id}</foreach></delete></mapper>

5. 服务层实现

CompanyCleanService:配置化驱动批量处理

@Service
@RequiredArgsConstructor
public class CompanyCleanService {private final CompanyCleanMapper companyCleanMapper;// 配置需要处理的表和字段private static final List<TableConfig> TABLE_CONFIGS = Arrays.asList(new TableConfig("contract_draft", "purchaser_id", "purchaser_name"),new TableConfig("invoice", "company_id", "company_name")// 按需添加其他表...);@Transactionalpublic void cleanDuplicateCompanies() {// 1. 查询所有需保留的公司List<CompanyRetainedInfo> retainedCompanies = companyCleanMapper.selectRetainedCompanies();for (CompanyRetainedInfo info : retainedCompanies) {// 2. 查询待删除的ID列表List<Long> idsToDelete = companyCleanMapper.selectIdsToDelete(info.getCompanyName());if (!idsToDelete.isEmpty()) {// 3. 更新所有关联表的外键引用TABLE_CONFIGS.forEach(config -> companyCleanMapper.updateForeignKeys(config, info.getRetainedId(), info.getRetainedName(), idsToDelete));// 4. 删除冗余公司记录companyCleanMapper.deleteCompanies(idsToDelete);}}}
}

关键设计说明

  1. 实体类与数据映射
    CompanyRetainedInfo 通过别名(AS retainedId)直接映射查询结果,避免额外转换。
    companyNameretainedName 字段值相同,但保留后者以明确语义。

  2. XML动态SQL优势
    <choose>:根据配置动态决定是否更新名称字段。
    <foreach>:自动展开ID列表为IN (id1, id2...),支持批量操作。
    ${}占位符:安全引用配置的表名和字段名(非用户输入,无注入风险)。

  3. 事务与性能优化
    @Transactional:保证“更新外键”和“删除公司”操作的原子性。
    索引建议:对sys_company.company_name和关联表的外键字段添加索引。


总结

通过 实体类封装配置化表关系MyBatis动态SQL,我们实现了一套可扩展的多表数据清洗方案。这种模式的核心在于:

  1. 抽象变化部分:将表和字段的差异收敛到配置类中。
  2. 复用不变逻辑:批量更新和删除操作由统一服务驱动。
  3. 最小化侵入性:新增表只需修改配置,无需改动核心逻辑。

该方案适用于用户中心、商品系统等存在外键关联的冗余数据处理场景,读者可结合实际需求调整配置和SQL逻辑。

相关文章:

在Spring Boot + MyBatis中优雅处理多表数据清洗:基于XML的配置化方案

问题背景 在实际业务中&#xff0c;我们常会遇到数据冗余问题。例如&#xff0c;一个公司表&#xff08;sys_company&#xff09;中存在多条相同公司名的记录&#xff0c;但只有一条有效&#xff08;del_flag0&#xff09;&#xff0c;其余需要删除。删除前需将关联表&#xf…...

Python教程(一):基本语法、流程控制、数据容器

Python&#xff08;一&#xff09; 文章目录 Python&#xff08;一&#xff09;一、基础语法二、数据类型2.1 字符串2.2 空值2.3 类型转换&运算符 三、流程控制3.1 条件判断3.2 循环3.2.1 while循环3.2.2 for循环 四、数据结构4.1 字符串str4.1.1 字符串的格式化输出4.1.1.…...

Spring Boot 3.x 核心注解详解与最佳实践

Spring Boot 3.x 核心注解详解与最佳实践 前言 随着Spring Boot 3.x的正式发布&#xff0c;这个基于Spring Framework 6的里程碑版本带来了诸多新特性。本文将深入剖析Spring Boot 3.x的核心注解体系&#xff0c;结合代码示例讲解其作用及使用场景&#xff0c;助您快速掌握新…...

【AI深度学习基础】PyTorch初探

引言 PyTorch 是由 Facebook 开源的深度学习框架&#xff0c;专门针对 GPU 加速的深度神经网络编程&#xff0c;它的核心概念包括张量&#xff08;Tensor&#xff09;、计算图和自动求导机制。PyTorch作为Facebook开源的深度学习框架&#xff0c;凭借其动态计算图和直观的API设…...

Windows下安装VMware Workstation 17并设置支持MacOS

VMware Workstation 17 介绍 VMware Workstation 17 是 VMware 公司推出的一款强大的桌面虚拟化软件&#xff0c;适用于 Windows 、 Linux 和FreeBSD等操作系统。它允许用户在单一物理计算机上创建、运行和管理多个虚拟机&#xff08;VM&#xff09;&#xff0c;每个虚拟机都可…...

Mysql-主从搭建如何指定库表同步以及新增库表同步

背景&#xff1a; 当主库数据量过大&#xff0c;从库仅需要同步A库的所有表&#xff0c;并且在后续运行中&#xff0c;又提出需要在从库新增B库的users表进行同步。本文会详细列出过程与具体命令&#xff0c;并告诉你其中的深坑&#xff01; 步骤一&#xff1a; 修改从库参数…...

爬虫逆向:脱壳工具Youpk的使用详解

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 1. Youpk 简介1.1 Youpk介绍1.2 Youpk支持场景1.3 Youpk基本流程1.4 使用 Youpk 脱壳步骤1.5 常用的脱壳工具对比2. Youpk 的安装与使用2.1 安装 Youpk2.2 使用 Youpk 脱壳3. 脱壳后的 Dex 文件分析3.1 使用 JADX 反编译…...

UE4 组件 (对话组件)

制作一个可以生成对话气泡&#xff0c;显示对话台词的简单组件。这个组件要的变量&#xff1a;台词&#xff08;外部传入&#xff09;。功能&#xff1a;开始对话&#xff08;生成气泡UI&#xff09; &#xff0c;结束对话。 一、对话组件创建 二、开始对话事件 1、注意这里获…...

LeetCode 2588.统计美丽子数组数目:前缀和 + 位运算(异或) + 哈希表

【LetMeFly】2588.统计美丽子数组数目&#xff1a;前缀和 位运算(异或) 哈希表 力扣题目链接&#xff1a;https://leetcode.cn/problems/count-the-number-of-beautiful-subarrays/ 给你一个下标从 0 开始的整数数组nums 。每次操作中&#xff0c;你可以&#xff1a; 选择…...

blender看不到导入的模型

参考&#xff1a;blender 快捷键 常见问题_blender材质预览快捷键-CSDN博客 方法一&#xff1a;视图-裁剪起点&#xff0c;设置一个很大的值 方法二&#xff1a;选中所有对象&#xff0c;对齐视图-视图对齐活动项-选择一个视图...

【慕课网wiki项目学习笔记01】Spring Boot 项目搭建

2-2 新建SpringBoot项目 一、创建SpringBoot项目 &#xff08;1&#xff09;在SpringBoot官网创建 &#xff08;2.1&#xff09;在 IDEA 中创建 Group&#xff1a;公司名 Artifact&#xff1a;项目名 创建成功后开始下载Maven依赖&#xff08;选择右下角的Import Changes&…...

后端架构模式之-BFF(Backend-For-Frontend)

Backend-for-Frontend&#xff08;BFF&#xff09; 的概念与意义 1. 什么是 Backend-for-Frontend&#xff08;BFF&#xff09;&#xff1f; Backend-for-Frontend&#xff08;简称 BFF&#xff09;是一种后端架构模式&#xff0c;它为特定的前端应用&#xff08;Web、移动端…...

【高分论文密码】AI大模型和R语言的全类型科研图形绘制,从画图、标注、改图、美化、组合、排序分解科研绘图每个步骤

在科研成果竞争日益激烈的当下&#xff0c;「一图胜千言」已成为高水平SCI期刊的硬性门槛——数据显示很多情况的拒稿与图表质量直接相关。科研人员普遍面临的工具效率低、设计规范缺失、多维数据呈现难等痛点&#xff0c;因此科研绘图已成为成果撰写中的至关重要的一个环节&am…...

vue3-pc-template后台管理之角色管理与功能权限配置实践

在开发企业级应用时&#xff0c;权限控制无疑是至关重要且不可或缺的一部分。合理的权限控制不仅能够有效保障系统的安全性&#xff0c;还能确保不同用户角色在系统中拥有合适的操作权限&#xff0c;从而提高系统的使用效率和稳定性。本文将详细介绍如何在 Vue3 项目中实现功能…...

Android Flow 示例

在Android开发的世界里&#xff0c;处理异步数据流一直是一个挑战。随着Kotlin的流行&#xff0c;Flow作为Kotlin协程库的一部分&#xff0c;为开发者提供了一种全新的方式来处理这些问题。今天&#xff0c;我将深入探讨Flow的设计理念&#xff0c;并通过具体的例子展示如何在实…...

前端文件加载耗时过长解决方案

从你的 Network (网络) 面板 看到&#xff0c;许多 JS 文件的加载时间较长&#xff08;1~2秒&#xff09;&#xff0c;可能的原因如下&#xff1a; ✅ 可能的原因 1. 过多的 JS 请求&#xff08;多个小文件加载&#xff09; 你当前页面加载了很多小 JS 文件&#xff08;addSi…...

Visual Studio 2022新建c语言项目的详细步骤

步骤1&#xff1a;点击创建新项目 步骤2&#xff1a;到了项目模板 --> 选择“控制台应用” (在window终端运行代码。默认打印"Hello World") --> 点击 “下一步” 步骤3&#xff1a;到了配置新项目模块 --> 输入“项目名称” --> 更改“位置”路径&…...

物联网系统搭建

实验项目名称 构建物联网系统 实验目的 掌握物联网系统的一般构建方法。 实验要求&#xff1a; 1&#xff0e;构建物联网系统&#xff0c;实现前后端的交互。 实验内容&#xff1a; CS模式MQTT&#xff08;不带数据分析处理功能&#xff09; 实现智能设备与应用客户端的交…...

PostgreSQL中的事务隔离

1. 事务隔离的概念 在数据库管理系统中&#xff0c;事务隔离是一项重要的功能&#xff0c;它能确保在并发访问数据库时事务之间能够独立运行&#xff0c;不会相互干扰。数据库系统通常支持不同级别的事务隔离&#xff0c;用来满足不同应用程序之间的需求。 2. 事务隔离的种类…...

嵌入式硬件设计SPI时需要注意什么?

嵌入式硬件设计SPI时需要注意什么? 1. 硬件设计注意事项 关键点注意事项1. 信号完整性- 缩短SCK、MOSI、MISO的走线长度,避免反射干扰。- 使用屏蔽线或差分信号(高速场景)。- 阻抗匹配(特别是高频信号,如50Ω端接)。2. 电源与地线- 电源去耦:每个SPI芯片的VCC附近放置0…...

mysql新手常见问题解决方法总结

1. 安装与配置问题 1.1 无法安装MySQL Server MySQL Server安装失败是新手常见的问题之一&#xff0c;以下是具体原因及解决方案&#xff1a; 系统要求不满足&#xff1a;MySQL对操作系统有最低版本要求&#xff0c;如Windows 7 SP1及以上、macOS 10.13及以上。若系统版本过…...

Unity3D 资源加载与卸载策略详解

前言 在Unity3D开发中&#xff0c;资源加载与卸载&#xff08;Asset Loading & Unloading&#xff09;是优化游戏性能、减少内存占用、提升用户体验的关键环节。本文将详细探讨Unity3D中的资源加载与卸载策略&#xff0c;并提供相关的技术详解和代码实现。 对惹&#xff…...

AcWing 蓝桥杯集训·每日一题2025·5526. 平衡细菌

5526. 平衡细菌 题意 给定一个序列 ( a i ) (a_i) (ai​)&#xff0c;每次操作可以选择一个位置 (p)&#xff0c;令从 ( a p ) (a_p) (ap​) 开始的每个数都加上一个以 (1) 或者 (-1) 为公差的从 ( 1 / − 1 ) (1 / -1) (1/−1) 开始的等差数列。求最小化让序列归零的操作…...

Android15请求动态申请存储权限完整示例

效果: 1.修改AndroidManifest.xml增加如下内容: <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-perm...

UniApp如何判断平台的多种方法(2025最新指南)

摘要&#xff1a;在UniApp跨平台开发中&#xff0c;精准判断运行环境是实现多端差异化的关键。本文将介绍6种判断平台的实用方法&#xff0c;涵盖编译时与运行时场景&#xff0c;助你轻松处理多端兼容问题。 一、为什么需要判断平台&#xff1f; 在UniApp跨平台开发中&#xf…...

unity学习62,尝试做第一个小游戏项目:flappy bird

目录 学习参考 1 创建1个unity 2D项目 1.1 2D项目模板选择 1.1.1 2D(built-in-Render pipeline) 1.1.2 universe 2D 1.1.3 这次选择 2D(built-in-Render pipeline) 1.2 创建项目 1.2.1 注意点 1.2.2 如果想修改项目名 2 导入美术资源包 2.1 下载一个flappy bird的…...

设计模式说明

23种设计模式说明 以下是常见的 23 种设计模式 分类及其核心思想、应用场景和简单代码示例&#xff0c;帮助你在实际开发中灵活运用&#xff1a; 一、创建型模式&#xff08;5种&#xff09; 解决对象创建问题&#xff0c;降低对象耦合。 1. 单例模式&#xff08;Singleton&…...

【STM32F103ZET6——库函数】11.捕获红外信号

目录 红外原理 数据码 引导码 连发码 配置捕获引脚 使能引脚时钟 配置定时器 使能定时器时钟 配置输入捕获 中断优先级分组 配置定时器4中断 定时器中断使能 使能定时器 重写定时器中断服务函数 清空定时器中断标志位 例程 例程说明 main.h main.c HongWai…...

unity调用本地部署deepseek全流程

unity调用本地部署deepseek全流程 deepseek本地部署 安装Ollama 搜索并打开Ollama官网[Ollama](https://ollama.com/download) 点击Download下载对应版本 下载后点击直接安装 安装deepseek大语言模型 官网选择Models 选择deepseek-r1&#xff0c;选择对应的模型&#xff0…...

Anaconda 部署 DeepSeek

可以通过 Anaconda 环境部署 DeepSeek 模型&#xff0c;但需结合 PyTorch 或 TensorFlow 等深度学习框架&#xff0c;并手动配置依赖项。 一、Anaconda 部署 DeepSeek 1. 创建并激活 Conda 环境 conda create -n deepseek python3.10 # 推荐 Python 3.8-3.10 conda activate…...