如何使用Spring Boot将CSV/Excel导入MSSQL,实体含关联对象如何处理
解决方案:Excel导入带关联外键的JPA实体到MSSQL数据库
错误根因
QuestionBank实体的status、quizLevel字段是关联实体对象类型,不是Integer类型,Lombok自动生成的setter方法入参为对应的实体类,无法直接传入ID整数- 你当前的
QuestionBank实体中未定义course相关字段,自然不存在setCourse方法,需先确认是否漏加了Course关联字段定义
实现步骤
1. 准备关联表的DAO层接口
首先新增对应关联实体的JPA Repository,用于查询关联对象:
// Status表查询接口 public interface StatusRepository extends JpaRepository<Status, Integer> { } // QuizLevel表查询接口 public interface QuizLevelRepository extends JpaRepository<QuizLevel, Integer> { } // 若确实需要course字段,先在QuestionBank实体中补全@ManyToOne关联定义,再新增对应CourseRepository
2. 注入依赖到业务类
在你处理导入逻辑的业务类中注入需要的Repository:
@Autowired private StatusRepository statusRepository; @Autowired private QuizLevelRepository quizLevelRepository; // 有Course关联的话也注入对应的CourseRepository
3. 修改赋值逻辑
通过Excel读取到的ID查询出关联实体对象后再赋值,同时增加异常校验避免非法ID:
questionBank.setAnswer(excelData.get(i)); questionBank.setContent(excelData.get(i + 1)); questionBank.setExplanation(excelData.get(i + 2)); questionBank.setOption1(excelData.get(i + 3)); questionBank.setOption2(excelData.get(i + 4)); questionBank.setOption3(excelData.get(i + 5)); questionBank.setOption4(excelData.get(i + 6)); // 处理状态关联 Integer statusId = Integer.valueOf(excelData.get(i + 8)); Status status = statusRepository.findById(statusId) .orElseThrow(() -> new RuntimeException("第"+(i/11 +1)+"行数据错误:不存在的状态ID:"+statusId)); questionBank.setStatus(status); // 处理课程关联(确认补全QuestionBank的course字段后放开使用) // Integer courseId = Integer.valueOf(excelData.get(i + 9)); // Course course = courseRepository.findById(courseId) // .orElseThrow(() -> new RuntimeException("第"+(i/11 +1)+"行数据错误:不存在的课程ID:"+courseId)); // questionBank.setCourse(course); // 处理难度关联 Integer levelId = Integer.valueOf(excelData.get(i + 10)); QuizLevel quizLevel = quizLevelRepository.findById(levelId) .orElseThrow(() -> new RuntimeException("第"+(i/11 +1)+"行数据错误:不存在的难度ID:"+levelId)); questionBank.setQuizLevel(quizLevel);
可选优化(适合大批量导入场景)
如果你能保证Excel中的关联ID都是合法存在的,可以使用JPA代理对象避免每次查询数据库,大幅提升导入效率:
// 先注入EntityManager @Autowired private EntityManager entityManager; // 赋值时直接生成代理对象,不会触发实际SELECT查询 Status status = entityManager.getReference(Status.class, statusId); questionBank.setStatus(status);
注意:如果关联表数据是预先配置好的、不需要导入时新增/修改,建议删除
@ManyToOne上的cascade=CascadeType.ALL配置,避免误操作修改关联表数据。
内容的提问来源于stack exchange,提问作者Nguyen Dinh Khanh
相关产品推荐
相关产品推荐

