nested exception is org.apache.ibatis.exceptions.TooManyResultsException:
Expected one result (or null) to be returned by selectOne(), but found: 2
原因: 同一机构存在多条相同的 study_instance_uid 记录,导致 selectOne() 报错。
selectList() 代替 selectOne()文件位置: DicomServiceImpl.java:468-494
修改前:
// 只用 selectOne,遇到多条记录会报错
LambdaQueryWrapper<StudyInfo> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(StudyInfo::getStudyInstanceUid, dicomData.getStudyInstanceUid());
StudyInfo studyInfo = studyInfoMapper.selectOne(wrapper); // ❌ TooManyResultsException
修改后:
// 使用 selectList,可以处理多条记录
LambdaQueryWrapper<StudyInfo> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(StudyInfo::getStudyInstanceUid, dicomData.getStudyInstanceUid());
wrapper.eq(StudyInfo::getInstitutionId, institutionId); // ✅ 添加机构ID条件
List<StudyInfo> existingStudies = studyInfoMapper.selectList(wrapper); // ✅ 使用 selectList
StudyInfo studyInfo = null;
if (existingStudies.isEmpty()) {
// 不存在,创建新记录
studyInfo = null;
} else if (existingStudies.size() == 1) {
// 存在一条,使用这条记录
studyInfo = existingStudies.get(0);
} else {
// 存在多条,记录警告并使用第一条
log.warn("检测到同一机构有多个相同的 study_instance_uid记录!...");
studyInfo = existingStudies.get(0);
}
优点:
文件位置: DicomServiceImpl.java:259-304
新增逻辑:
// 检查是否已存在该study
LambdaQueryWrapper<StudyInfo> studyQuery = new LambdaQueryWrapper<>();
studyQuery.eq(StudyInfo::getStudyInstanceUid, studyInstanceUid);
studyQuery.eq(StudyInfo::getInstitutionId, institutionId); // ✅ 添加机构ID条件
List<StudyInfo> existingStudies = studyInfoMapper.selectList(studyQuery);
if (!existingStudies.isEmpty()) {
// 已存在,更新记录而不是新建
studyInfo = existingStudies.get(0);
if (existingStudies.size() > 1) {
log.warn("检测到同一机构有多个相同的 study_instance_uid记录!...");
}
// 更新统计信息
studyInfo.setSeriesCount(studyInfo.getSeriesCount() + newSeriesCount);
studyInfo.setImageCount(studyInfo.getImageCount() + newFileCount);
studyInfo.setUploadTime(LocalDateTime.now());
studyInfo.setUpdateTime(LocalDateTime.now());
studyInfoMapper.updateById(studyInfo); // ✅ 更新而不是插入
} else {
// 不存在,创建新记录
studyInfo = new StudyInfo();
studyInfoMapper.insert(studyInfo);
}
优点:
文件位置: DicomAsyncServiceImpl.java:162-166
修改前:
LambdaQueryWrapper<SeriesInfo> query = new LambdaQueryWrapper<>();
query.eq(SeriesInfo::getSopInstanceUid, dicomData.getSopInstanceUID());
SeriesInfo existing = seriesInfoMapper.selectOne(query); // ❌ 可能报错
修改后:
LambdaQueryWrapper<SeriesInfo> query = new LambdaQueryWrapper<>();
query.eq(SeriesInfo::getSopInstanceUid, dicomData.getSopInstanceUID());
query.eq(SeriesInfo::getInstitutionId, studyInfo.getInstitutionId()); // ✅ 添加机构ID条件
SeriesInfo existing = seriesInfoMapper.selectOne(query); // 现在可以安全使用
优点:
institution_id 条件,确保不同机构数据隔离SQL文件: doc/sql/add_study_unique_constraint.sql
约束定义:
ALTER TABLE study_info
ADD UNIQUE KEY uk_study_instance (institution_id, study_instance_uid);
作用:
study_instance_uidSQL文件: doc/sql/cleanup_duplicate_studies.sql
提供的方案:
✅ 代码修改已全部完成,包括:
DicomServiceImpl.java - 上传逻辑DicomAsyncServiceImpl.java - 异步解析逻辑institution_id 条件selectList() 处理多条记录执行清理脚本:
mysql -u root -p qconline < doc/sql/cleanup_duplicate_studies.sql
建议步骤:
执行约束脚本:
mysql -u root -p qconline < doc/sql/add_study_unique_constraint.sql
验证约束:
SHOW INDEX FROM study_info WHERE Key_name = 'uk_study_instance';
# 重启 Spring Boot 应用
mvn spring-boot:run
测试场景:
| 场景 | 结果 |
|---|---|
| 同一机构重复上传 | ❌ TooManyResultsException |
| 不同机构重复上传 | ⚠️ 数据可能混乱 |
| 查询接口 | ❌ 报错 |
| 场景 | 结果 |
|---|---|
| 同一机构重复上传 | ✅ 更新已有记录,不报错 |
| 不同机构重复上传 | ✅ 数据完全隔离 |
| 查询接口 | ✅ 正常返回数据 |
| 数据库约束 | ✅ 防止新的重复数据 |
可能的原因:
代码层面(已完成):
selectList() 而不是 selectOne()institution_id 条件确保数据隔离数据库层面(待执行):
uk_study_instanceINFO - 找到已存在的检查记录,将更新: studyId=STY001, studyInstanceUid=1.2.3...
INFO - 更新检查记录: studyId=STY001, 新的seriesCount=5, 新的imageCount=120
WARN - 检测到同一机构有多个相同的 study_instance_uid记录!
studyInstanceUid=1.2.3..., institutionId=INST001, count=2
WARN - 这可能需要数据清理,将使用第一条记录进行更新
看到警告日志时:
A: 因为同一机构有多个相同的 study_instance_uid 记录,selectOne() 期望返回0或1条记录,但实际返回了多条。
A: 不会。修改后使用 selectList(),可以处理多条记录的情况。如果有多条,会记录警告并使用第一条。
A: 不会。查询时添加了 institution_id 条件,不同机构的数据是完全隔离的。
A: 执行 cleanup_duplicate_studies.sql,提供了5个方案,建议先备份再删除。
A: 从数据库层面防止重复,如果尝试插入重复的 institution_id + study_instance_uid,数据库会直接拒绝。
DicomServiceImpl.java - 上传逻辑DicomAsyncServiceImpl.java - 异步解析逻辑doc/sql/add_study_unique_constraint.sql - 添加唯一约束doc/sql/cleanup_duplicate_studies.sql - 清理重复数据doc/重复上传文件处理逻辑说明.md - 详细说明文档