Обработка исключений пакета Spring, отправленная как ResponseEntity
Я новичок в Spring boot, я тренируюсь в небольшом проекте с пакетом Spring , чтобы получить опыт. Вот мой контекст: у меня есть 2 файла csv , один содержит сотрудников , другой содержит всех менеджеров компании. Мне нужно читать файлы, а затем добавлять каждую запись в базу данных . Чтобы сделать это простым, мне просто нужно вызвать конечную точку с моего контроллера, загрузить мой CSV-файл (многочастный файл), после чего задание начнется. Я действительно смог это сделать, моя проблема заключается в следующем.
Мне нужно управлять несколькими типами проверки (я использую проверку jsr 380 для своих объектов, и я также должен проверить бизнес-исключение ). Типом бизнес-исключения может быть следующее правило: Сотрудник находится под контролем менеджера своего отдела ( сотрудник не может контролироваться менеджером, если он не находится в том же отделе, в противном случае должно быть выдано исключение ). Таким образом, для ошибочных записей с некоторым недопустимым или «нелогичным» вводом я должен пропустить их (не сохранять в базе данных), но сохранить их на карте или в списке , которые должны быть отправлены клиенту как сущность ответов .. Следовательно, клиент будет знать, какую строку нужно исправить. Я полагаю, мне нужно взглянуть на ** Слушатели **, но я действительно не могу хранить исключения на карте или в списке, а затем отправлять их как ResponseEntity. Ниже пример того, чего я хочу достичь.
СотрудникBatchConfig.java
@Configuration
@EnableBatchProcessing
@AllArgsConstructor
public class EmployeeBatchConfig {
private JobBuilderFactory jobBuilderFactory;
private StepBuilderFactory stepBuilderFactory;
private EmployeeRepository employeeRepository;
private EmployeeItemWriter employeeItemWriter;
@Bean
@StepScope
public FlatFileItemReader<EmployeeDto> itemReader(@Value("#
{jobParameters[fullPathFileName]}") final String pathFile) {
FlatFileItemReader<EmployeeDto> flatFileItemReader = new
FlatFileItemReader<>();
flatFileItemReader.setResource(new FileSystemResource(new
File(pathFile)));
flatFileItemReader.setName("CSV-Reader");
flatFileItemReader.setLinesToSkip(1);
flatFileItemReader.setLineMapper(lineMapper());
return flatFileItemReader;
}
private LineMapper<EtudiantDto> lineMapper() {
DefaultLineMapper<EtudiantDto> lineMapper = new DefaultLineMapper<>
();
DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer();
lineTokenizer.setDelimiter(",");
lineTokenizer.setStrict(false);
lineTokenizer.setNames("Username", "lastName", "firstName",
"departement", "supervisor");
BeanWrapperFieldSetMapper<EmployeeDto> fieldSetMapper = new
BeanWrapperFieldSetMapper<>();
fieldSetMapper.setTargetType(EmployeeDto.class);
lineMapper.setLineTokenizer(lineTokenizer);
lineMapper.setFieldSetMapper(fieldSetMapper);
return lineMapper;
}
@Bean
public EmployeeProcessor processor() {
return new EmployeeProcessor(); /*Create a bean processor to skip
invalid rows*/
}
@Bean
public RepositoryItemWriter<Employee> writer() {
RepositoryItemWriter<Employee> writer = new RepositoryItemWriter<>();
writer.setRepository(employeeRepository);
writer.setMethodName("save");
return writer;
}
@Bean
public Step step1(FlatFileItemReader<EmployeeDto> itemReader) {
return stepBuilderFactory.get("slaveStep").<EmployeeDto,
Employee>chunk(5)
.reader(itemReader)
.processor(processor())
.writer(employeeItemWriter)
.faultTolerant()
.listener(skipListener())
.skip(SkipException.class)
.skipLimit(10)
.skipPolicy(skipPolicy())
.build();
}
@Bean
@Qualifier("executeJobEmployee")
public Job runJob(FlatFileItemReader<Employee> itemReader) {
return jobBuilderFactory
.get("importEmployee")
.flow(step1(itemReader))
.end()
.build();
}
@Bean
public SkipPolicy skipPolicy(){
return new ExceptionSkipPolicy();
}
@Bean
public SkipListener<EmployeeDto, Employee> skipListener(){
return new StepSkipListener();
}
/*@Bean
public ExecutionContext executionContext(){
return new ExecutionContext();
}*/
}
СотрудникПроцессор.java
public class EmployeeProcessor implements ItemProcessor<EmployeeDto,
Employee>{
@Autowired
private SupervisorService managerService;
@Override
public Employee process(@Valid EmployeeDto item) throws Exception,
SkipException {
ManagerDto manager =
SupervisorService.findSupervisorById(item.getSupervisor());
//retrieve the manager of the employee and compare departement
if(!(manager.getDepartement().equals(item.getDepartement()))) {
throw new SkipException("Manager Invalid", item);
//return null;
}
return ObjectMapperUtils.map(item, Employee.class);
}
}
MySkipPolicy.java
public class MySkipPolicy implements SkipPolicy {
@Override
public boolean shouldSkip(Throwable throwable, int i) throws
SkipLimitExceededException {
return true;
}
}
StepSkipListenerPolicy.java
public class StepSkipListener implements SkipListener<EmployeeDto,
Number> {
@Override // item reader
public void onSkipInRead(Throwable throwable) {
System.out.println("In OnSkipReader");
}
@Override // item writter
public void onSkipInWrite(Number item, Throwable throwable) {
System.out.println("Nooooooooo ");
}
//@SneakyThrows
@Override // item processor
public void onSkipInProcess(@Valid EmployeeDto employee, Throwable
throwable){
System.out.println("Process... ");
/* I guess this is where I should work, but how do I deal with the
exception occur? How do I know which exception I would get ? */
}
}
SkipException.java
public class SkipException extends Exception {
private Map<String, EmployeeDto> errors = new HashMap<>();
public SkipException(String errorMessage, EmployeeDto employee) {
super();
this.errors.put(errorMessage, employee);
}
public Map<String, EmployeeDto> getErrors() {
return this.errors;
}
}
JobController.java
@RestController
@RequestMapping("/upload")
public class JobController {
@Autowired
private JobLauncher jobLauncher;
@Autowired
@Qualifier("executeJobEmployee")
private Job job;
private final String EMPLOYEE_FOLDER = "C:/Users/Project/Employee/";
@PostMapping("/employee")
public ResponseEntity<Object> importEmployee(@RequestParam("file")
MultipartFile multipartFile) throws JobInterruptedException,
SkipException, IllegalStateException, IOException,
FlatFileParseException{
try {
String fileName = multipartFile.getOriginalFilename();
File fileToImport= new File(EMPLOYEE_FOLDER + fileName);
multipartFile.transferTo(fileToImport);
JobParameters jobParameters = new JobParametersBuilder()
.addString("fullPathFileName", EMPLOYEE_FOLDER + fileName)
.addLong("startAt", System.currentTimeMillis())
.toJobParameters();
JobExecution jobExecution = this.jobLauncher.run(job,
jobParameters);
ExecutionContext executionContext =
jobExecution.getExecutionContext();
System.out.println("My Skiped items : " +
executionContext.toString());
} catch (ConstraintViolationException | FlatFileParseException |
JobRestartException | JobInstanceAlreadyCompleteException |
JobParametersInvalidException |
JobExecutionAlreadyRunningException e) {
e.printStackTrace();
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>("Employee inserted succesfully",
HttpStatus.OK);
}
}
1 ответ
Это требование заставляет вашу реализацию ждать завершения задания, прежде чем возвращать веб-ответ, что не является типичным способом запуска пакетных заданий из веб-запросов. Как правило, поскольку пакетные задания могут выполняться в течение нескольких минут/часов, они запускаются в фоновом режиме, а идентификатор задания возвращается клиенту для последующей проверки состояния.
В весенней партии
Пропущенные элементы обычно сохраняются где-то для последующего анализа (например, таблица, файл или контекст выполнения задания). В вашем случае вам нужно отправить их обратно в веб-ответ, чтобы вы могли прочитать их из магазина по вашему выбору, прежде чем возвращать их, прикрепленные к веб-ответу. В псевдокоде вашего контроллера это должно выглядеть примерно так:
- run the job and wait for its termination (the skip listener would write skipped items in the storage of your choice)
- get skipped items from storage
- return web response
Например, если вы решите хранить пропущенные элементы в контексте выполнения задания, вы можете сделать что-то подобное в своем контроллере:
JobExecution jobExecution = jobLauncher.run(job, jobParameters);
ExecutionContext executionContext = jobExecution.getExecutionContext();
// get skipped items from the execution context
// return the web response