Problem background
In actual business, we often encounter data redundancy problems. For example, a company table (sys_company
There are multiple records of the same company name in ) but only one is valid (del_flag=0
), the rest need to be deleted. Before deletion, you need to add foreign key fields of the associated table (such as contract draft table, invoice table, etc.) (such aspurchaser_id
) Replace with the ID that keeps the record. This type of problem usually involves dynamic updates of multiple tables and multiple fields. How to implement them efficiently and safely?
Solution
We will do it through the following steps:
- Configurable driver: Use configuration classes to declare tables and fields that need to be processed to avoid hard coding.
- Dynamic SQL updates: Batch update and deletion are realized through MyBatis XML.
- Transaction consistency: Ensure that all operations are performed atomically.
Implementation steps
1. Define entity classes
CompanyRetainedInfo
: Encapsulate the company information that needs to be retained
import ; @Data public class CompanyRetainedInfo { private String companyName; // Company Name private Long retainedId; // The company ID to be retained (record of del_flag=0) private String retainedName; // The company name to be retained (consistent with companyName)}
• effect: Map query results, pass the ID and name of the reserved record.
• Lombok:@Data
Automatically generate Getter/Setter andtoString()
method.
2. Define configuration class
TableConfig
: Declare the table and foreign key relationship to be processed
public class TableConfig { private String tableName; // Table name (such as contract_draft) private String idColumn; // Foreign key ID field (such as purchaseser_id) private String nameColumn; // Name field (such as purchaseser_name, probably null) // Constructor + Getter/Setter public TableConfig(String tableName, String idColumn, String nameColumn) { = tableName; = idColumn; = nameColumn; } }
3. Write the MyBatis Mapper interface
CompanyCleanMapper
: Define the data operation interface (No annotation, pure XML mapping)
@Mapper public interface CompanyCleanMapper { // Query the company information to be retained (del_flag=0) List<CompanyRetainedInfo> selectRetainedCompanies(); // Check the ID list to be deleted based on the company name (del_flag!=0) List<Long> selectIdsToDelete(String companyName); // Update foreign key references to the associated table void updateForeignKeys( @Param("config") TableConfig config, @Param("retainedId") Long retainedId, @Param("retainedName") String retainedName, @Param("ids") List<Long> ids ); // Delete redundant company records void deleteCompanies(@Param("ids") List<Long> ids); }
4. Implement XML mapping files
: Define dynamic SQL logic
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-////DTD Mapper 3.0//EN" "/dtd/"> <mapper namespace=""> <!-- Inquiry of the company to be retained --> <select 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> <!-- Query what to deleteIDList --> <select resultType="long"> SELECT id FROM sys_company WHERE company_name = #{companyName} AND del_flag != 0 </select> <!-- Dynamically update foreign key references --> <update > UPDATE ${} SET <choose> <when test=" != null"> <!-- Updated at the same timeIDand name fields --> ${} = #{retainedId}, ${} = #{retainedName} </when> <otherwise> <!-- Update onlyIDFields --> ${} = #{retainedId} </otherwise> </choose> WHERE ${} IN <foreach item="id" collection="ids" open="(" separator="," close=")"> #{id} </foreach> </update> <!-- Delete company records in batches --> <delete > DELETE FROM sys_company WHERE id IN <foreach item="id" collection="ids" open="(" separator="," close=")"> #{id} </foreach> </delete> </mapper>
5. Service layer implementation
CompanyCleanService
: Configurable driver batch processing
@Service @RequiredArgsConstructor public class CompanyCleanService { private final CompanyCleanMapper companyCleanMapper; // Configure tables and fields to be processed private static final List<TableConfig> TABLE_CONFIGS = ( new TableConfig("contract_draft", "purchaser_id", "purchaser_name"), new TableConfig("invoice", "company_id", "company_name") // Add other tables as needed... ); @Transactional public void cleanDuplicateCompanies() { // 1. Inquiry of all companies that need to be retained List<CompanyRetainedInfo> retainedCompanies = (); for (CompanyRetainedInfo info : retainedCompanies) { // 2. Query the list of IDs to be deleted List<Long> idsToDelete = (()); if (!()) { // 3. Update foreign key references to all associated tables TABLE_CONFIGS.forEach(config -> ( config, (), (), idsToDelete ) ); // 4. Delete redundant company records (idsToDelete); } } } }
Key Design Notes
Entity class and data mapping
•CompanyRetainedInfo
By alias (AS retainedId
) Directly map query results to avoid additional conversions.
•companyName
andretainedName
The field values are the same, but the latter is preserved for explicit semantics.Advantages of XML Dynamic SQL
•<choose>
: Dynamically decide whether to update the name field based on the configuration.
•<foreach>
: Automatically expand the ID list asIN (id1, id2...)
, supports batch operation.
•${}
Placeholder: The table name and field name of the secure reference configuration (non-user input, no injection risk).Transaction and performance optimization
•@Transactional
: Ensure the atomicity of "update foreign keys" and "delete company" operations.
• Indexing suggestions:rightsys_company.company_name
Add indexes to the foreign key fields of the associated table.
Summarize
passEntity encapsulation、Configurable table relationshipsandMyBatis Dynamic SQL, We implement a set of scalable multi-table data cleaning solutions. The core of this model is:
- Abstract change part: Converge the differences between tables and fields into the configuration class.
- Reuse unchanged logic: Batch updates and deletion operations are driven by unified services.
- Minimize invasiveness: Adding new tables only requires modifying the configuration, no need to change the core logic.
This solution is suitable for redundant data processing scenarios such as user centers and product systems where foreign key relationships exist. Readers can adjust configuration and SQL logic based on actual needs.
The above is the detailed content of the implementation steps for elegantly handling multi-table data cleaning in SpringBoot+MyBatis. For more information about SpringBoot MyBatis multi-table data cleaning, please pay attention to my other related articles!