在SpringBoot+MyBatis中優(yōu)雅處理多表數(shù)據(jù)清洗的實現(xiàn)步驟
問題背景
在實際業(yè)務(wù)中,我們常會遇到數(shù)據(jù)冗余問題。例如,一個公司表(sys_company
)中存在多條相同公司名的記錄,但只有一條有效(del_flag=0
),其余需要刪除。刪除前需將關(guān)聯(lián)表(如合同草稿表、發(fā)票表等)的外鍵字段(如purchaser_id
)替換為保留記錄的ID。這類問題通常涉及多表、多字段的動態(tài)更新,如何高效且安全地實現(xiàn)?
解決方案
我們將通過以下步驟實現(xiàn):
- 配置化驅(qū)動:用配置類聲明需要處理的表和字段,避免硬編碼。
- 動態(tài)SQL更新:通過MyBatis XML實現(xiàn)批量更新和刪除。
- 事務(wù)一致性:確保所有操作原子化執(zhí)行。
實現(xiàn)步驟
1. 定義實體類
CompanyRetainedInfo
:封裝需保留的公司信息
import lombok.Data; @Data public class CompanyRetainedInfo { private String companyName; // 公司名稱 private Long retainedId; // 需保留的公司ID(del_flag=0的記錄) private String retainedName; // 需保留的公司名稱(與companyName一致) }
• 作用:映射查詢結(jié)果,傳遞保留記錄的ID和名稱。
• Lombok:@Data
自動生成Getter/Setter和toString()
方法。
2. 定義配置類
TableConfig
:聲明需處理的表和外鍵關(guān)系
public class TableConfig { private String tableName; // 表名(如contract_draft) private String idColumn; // 外鍵ID字段(如purchaser_id) private String nameColumn; // 名稱字段(如purchaser_name,可能為null) // 構(gòu)造器 + Getter/Setter public TableConfig(String tableName, String idColumn, String nameColumn) { this.tableName = tableName; this.idColumn = idColumn; this.nameColumn = nameColumn; } }
3. 編寫MyBatis Mapper接口
CompanyCleanMapper
:定義數(shù)據(jù)操作接口(無注解,純XML映射)
@Mapper public interface CompanyCleanMapper { // 查詢需保留的公司信息(del_flag=0) List<CompanyRetainedInfo> selectRetainedCompanies(); // 根據(jù)公司名查詢待刪除的ID列表(del_flag!=0) List<Long> selectIdsToDelete(String companyName); // 更新關(guān)聯(lián)表的外鍵引用 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. 實現(xiàn)XML映射文件
CompanyCleanMapper.xml
:定義動態(tài)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 retainedName FROM sys_company WHERE del_flag = 0 AND company_name IN ( SELECT company_name FROM sys_company GROUP BY company_name HAVING COUNT(*) > 1 AND SUM(del_flag = 0) = 1 ) </select> <!-- 查詢待刪除的ID列表 --> <select id="selectIdsToDelete" resultType="long"> SELECT id FROM sys_company WHERE company_name = #{companyName} AND del_flag != 0 </select> <!-- 動態(tài)更新外鍵引用 --> <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_company WHERE id IN <foreach item="id" collection="ids" open="(" separator="," close=")"> #{id} </foreach> </delete> </mapper>
5. 服務(wù)層實現(xiàn)
CompanyCleanService
:配置化驅(qū)動批量處理
@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") // 按需添加其他表... ); @Transactional public 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. 更新所有關(guān)聯(lián)表的外鍵引用 TABLE_CONFIGS.forEach(config -> companyCleanMapper.updateForeignKeys( config, info.getRetainedId(), info.getRetainedName(), idsToDelete ) ); // 4. 刪除冗余公司記錄 companyCleanMapper.deleteCompanies(idsToDelete); } } } }
關(guān)鍵設(shè)計說明
實體類與數(shù)據(jù)映射
•CompanyRetainedInfo
通過別名(AS retainedId
)直接映射查詢結(jié)果,避免額外轉(zhuǎn)換。
•companyName
和retainedName
字段值相同,但保留后者以明確語義。XML動態(tài)SQL優(yōu)勢
•<choose>
:根據(jù)配置動態(tài)決定是否更新名稱字段。
•<foreach>
:自動展開ID列表為IN (id1, id2...)
,支持批量操作。
•${}
占位符:安全引用配置的表名和字段名(非用戶輸入,無注入風(fēng)險)。事務(wù)與性能優(yōu)化
•@Transactional
:保證“更新外鍵”和“刪除公司”操作的原子性。
• 索引建議:對sys_company.company_name
和關(guān)聯(lián)表的外鍵字段添加索引。
總結(jié)
通過 實體類封裝、配置化表關(guān)系 和 MyBatis動態(tài)SQL,我們實現(xiàn)了一套可擴展的多表數(shù)據(jù)清洗方案。這種模式的核心在于:
- 抽象變化部分:將表和字段的差異收斂到配置類中。
- 復(fù)用不變邏輯:批量更新和刪除操作由統(tǒng)一服務(wù)驅(qū)動。
- 最小化侵入性:新增表只需修改配置,無需改動核心邏輯。
該方案適用于用戶中心、商品系統(tǒng)等存在外鍵關(guān)聯(lián)的冗余數(shù)據(jù)處理場景,讀者可結(jié)合實際需求調(diào)整配置和SQL邏輯。
以上就是在SpringBoot+MyBatis中優(yōu)雅處理多表數(shù)據(jù)清洗的實現(xiàn)步驟的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot MyBatis多表數(shù)據(jù)清洗的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
spring boot 本地圖片不能加載(圖片路徑)的問題及解決方法
這篇文章主要介紹了spring boot 本地圖片不能加載(圖片路徑)的問題,解決的辦法其實很簡單,只要寫一個配置文件,也就是圖片位置的轉(zhuǎn)化器,原理是虛擬一個在服務(wù)器上的文件夾,與本地圖片的位置進(jìn)行匹配。需要的朋友可以參考下2018-04-04Java獲取本機IP地址的方法代碼示例(內(nèi)網(wǎng)、公網(wǎng))
在IT領(lǐng)域獲取本機IP地址是一項基礎(chǔ)但重要的任務(wù),特別是在網(wǎng)絡(luò)編程、遠(yuǎn)程協(xié)作和設(shè)備通信中,這篇文章主要給大家介紹了關(guān)于Java獲取本機IP地址的方法(內(nèi)網(wǎng)、公網(wǎng)),需要的朋友可以參考下2024-07-07SpringBoot淺析安全管理之基于數(shù)據(jù)庫認(rèn)證
在真實的項目中,用戶的基本信息以及角色等都存儲在數(shù)據(jù)庫中,因此需要從數(shù)據(jù)庫中獲取數(shù)據(jù)進(jìn)行認(rèn)證和授權(quán)2022-08-08springboot @Controller和@RestController的區(qū)別及應(yīng)用詳解
這篇文章主要介紹了springboot @Controller和@RestController的區(qū)別及應(yīng)用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11springMVC+jersey實現(xiàn)跨服務(wù)器文件上傳
這篇文章主要介紹了springMVC+jersey實現(xiàn)跨服務(wù)器文件上傳,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-08-08