From 36fb767b8ed60317b764d267cb959963a48021ff Mon Sep 17 00:00:00 2001 From: lapuda <577732344@qq.com> Date: Tue, 21 Jul 2026 16:49:10 +0800 Subject: [PATCH] fix: avoid druid returning parse errors in import --- .../ExternalJobImportServiceImpl.java | 36 +++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/externalimport/ExternalJobImportServiceImpl.java b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/externalimport/ExternalJobImportServiceImpl.java index 5d418e2..954ff0a 100644 --- a/ruoyi-bussiness/src/main/java/com/ruoyi/cms/externalimport/ExternalJobImportServiceImpl.java +++ b/ruoyi-bussiness/src/main/java/com/ruoyi/cms/externalimport/ExternalJobImportServiceImpl.java @@ -3,12 +3,15 @@ package com.ruoyi.cms.externalimport; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; +import java.sql.PreparedStatement; import java.sql.Timestamp; import java.time.LocalDate; import java.util.ArrayList; @@ -326,29 +329,48 @@ public class ExternalJobImportServiceImpl implements IExternalJobImportService { emptyToNull(candidate.getCompanyScaleCode()), emptyToNull(candidate.getCompanyNatureCode()), emptyToNull(candidate.getContactPerson()), SYSTEM_USER, companyId); return companyId; } - Long companyId = jdbcTemplate.queryForObject( + return insertAndReturnGeneratedKey( "INSERT INTO company (name, location, industry, scale, company_nature, description, contact_person, del_flag, create_by, create_time, company_status) " - + "VALUES (?, ?, ?, ?, ?, ?, ?, '0', ?, CURRENT_TIMESTAMP, '0') RETURNING company_id", - Long.class, candidate.getCompanyName(), emptyToNull(candidate.getLocation()), emptyToNull(firstNonBlank(candidate.getIndustrySub(), candidate.getIndustryType())), + + "VALUES (?, ?, ?, ?, ?, ?, ?, '0', ?, CURRENT_TIMESTAMP, '0')", + "company_id", candidate.getCompanyName(), emptyToNull(candidate.getLocation()), emptyToNull(firstNonBlank(candidate.getIndustrySub(), candidate.getIndustryType())), emptyToNull(candidate.getCompanyScaleCode()), emptyToNull(candidate.getCompanyNatureCode()), emptyToNull(candidate.getCompanyDescription()), emptyToNull(candidate.getContactPerson()), SYSTEM_USER); - return companyId; } private Long insertJob(ExternalJobCandidate candidate, Long companyId, String batchId) { - return jdbcTemplate.queryForObject("INSERT INTO job (job_title, min_salary, max_salary, education, experience, company_name, " + return insertAndReturnGeneratedKey("INSERT INTO job (job_title, min_salary, max_salary, education, experience, company_name, " + "job_location, job_location_area_code, posting_date, vacancies, \"view\", company_id, is_hot, is_urgent, apply_num, " + "description, is_publish, data_source, job_url, job_category, job_address, job_status, del_flag, create_by, create_time, " + "external_import_flag, external_source_code, external_source_name, job_fingerprint, job_content_fingerprint, " + "external_batch_id, external_first_seen_at, external_last_seen_at) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, '0', '0', '0', ?, 1, ?, ?, ?, ?, '0', '0', ?, CURRENT_TIMESTAMP, " - + "'1', ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) RETURNING job_id", - Long.class, + + "'1', ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)", + "job_id", candidate.getJobTitle(), parseMoney(candidate.getSalaryLow()), parseMoney(candidate.getSalaryHigh()), candidate.getEducationCode(), candidate.getExperienceCode(), candidate.getCompanyName(), emptyToNull(candidate.getLocation()), emptyToNull(candidate.getAreaCode()), timestamp(candidate.getPostingDate()), vacancies(candidate.getVacanciesText()), companyId, candidate.getDescription(), candidate.getSourceName(), candidate.getJobUrl(), emptyToNull(candidate.getJobCategory()), emptyToNull(candidate.getJobAddress()), SYSTEM_USER, candidate.getSourceCode(), candidate.getSourceName(), candidate.getJobFingerprint(), candidate.getContentFingerprint(), batchId); } + /** + * 通过 JDBC 标准生成键接口读取 identity 主键,避免将 HighGo 的 {@code RETURNING} + * 语法交给 Druid StatFilter 解析并在应用错误日志中产生伪异常。 + */ + private Long insertAndReturnGeneratedKey(String sql, String keyColumn, Object... args) { + KeyHolder keyHolder = new GeneratedKeyHolder(); + int affectedRows = jdbcTemplate.update(connection -> { + PreparedStatement statement = connection.prepareStatement(sql, new String[] {keyColumn}); + for (int index = 0; index < args.length; index++) { + statement.setObject(index + 1, args[index]); + } + return statement; + }, keyHolder); + Number key = keyHolder.getKey(); + if (affectedRows != 1 || key == null) { + throw new IllegalStateException("新增记录后未获得主键: " + keyColumn); + } + return key.longValue(); + } + private void updateExistingJob(Long jobId, ExternalJobCandidate candidate, Long companyId, String batchId) { jdbcTemplate.update("UPDATE job SET job_title=?, min_salary=?, max_salary=?, education=?, experience=?, company_name=?, " + "job_location=?, job_location_area_code=?, posting_date=?, vacancies=?, company_id=?, description=?, is_publish=1, "