Гость
Map
Форумы / Java [игнор отключен] [закрыт для гостей] / Подскажите как оптимизировать код / 3 сообщений из 3, страница 1 из 1
12.12.2021, 11:30
    #40119397
bobo96
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Подскажите как оптимизировать код
Суть такая: есть несколько csv файлов, информацию из которых нужно засунуть в БД. Реализовал это с помощью spring-batch, но как мне кажется саму реализацию можно сделать красивее..
Надеюсь на ваши подсказки.

1. Batch конфиг:
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
@Slf4j
@Configuration
@EnableBatchProcessing
@RequiredArgsConstructor
public class BatchConfig {

  @Value("${custom.csv.tags.file-name}")
  private String fileNameTags;

  @Value("${custom.csv.authors.file-name}")
  private String fileNameAuthors;

  @Value("${custom.csv.books.file-name}")
  private String fileNameBooks;

  private final JobBuilderFactory jobBuilderFactory;
  private final StepBuilderFactory stepBuilderFactory;
  private final DataSource dataSource;

  @Bean
  public TagBatchImpl getTagData() {
    return new TagBatchImpl(
            dataSource,
            fileNameTags
    );
  }

  @Bean
  public AuthorsBatchImpl getAuthorData() {
    return new AuthorsBatchImpl(
            dataSource,
            fileNameAuthors
    );
  }

  @Bean
  public BooksBatchImpl getBookData() {
    return new BooksBatchImpl(
            dataSource,
            fileNameBooks
    );
  }

  @Bean
  public Step tagStep() {
    return stepBuilderFactory.get("tag-step")
            .<TagDto, TagDto>chunk(10)
            .reader(getTagData().getReader())
            .writer(getTagData().getWriter())
            .build();
  }

  @Bean
  public Step authorStep() {
    return stepBuilderFactory.get("author-step")
            .<AuthorDto, AuthorDto>chunk(10)
            .reader(getAuthorData().getReader())
            .writer(getAuthorData().getWriter())
            .build();
  }

  @Bean
  public Step bookStep() {
    return stepBuilderFactory.get("book-step")
            .<BookDto, BookDto>chunk(10)
            .reader(getBookData().getReader())
            .writer(getBookData().getWriter())
            .build();
  }

  @Bean
  public Job job() {
    return jobBuilderFactory.get("job")
            .start(tagStep())
            .next(authorStep())
            .next(bookStep())
            .build();
  }

}



2. Абстракция для создания Step'ов:
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
public class AbstractBatchImpl<T> {

  private final DataSource dataSource;
  private final String fileName;
  private final Class<T> type;

  @Setter
  private String query;
  @Setter
  private String[] headers;

  public AbstractBatchImpl(
          DataSource dataSource,
          String fileName,
          Class<T> type) {
    this.dataSource = dataSource;
    this.fileName = fileName;
    this.type = type;
  }

  public FlatFileItemReader<T> getReader() {
    FlatFileItemReader<T> reader = new FlatFileItemReader<>();
    reader.setLinesToSkip(1);
    reader.setResource(new FileSystemResource(fileName));

    DefaultLineMapper<T> customerLineMapper = new DefaultLineMapper<>();

    DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer();
    tokenizer.setNames(headers);

    customerLineMapper.setLineTokenizer(tokenizer);
    customerLineMapper.setFieldSetMapper(new BeanWrapperFieldSetMapper<>() {{
      setTargetType(type);
    }});
    customerLineMapper.afterPropertiesSet();
    reader.setLineMapper(customerLineMapper);
    return reader;
  }

  @SuppressWarnings({ "rawtypes", "unchecked" })
  public JdbcBatchItemWriter<T> getWriter() {
    JdbcBatchItemWriter<T> itemWriter = new JdbcBatchItemWriter<>();

    itemWriter.setDataSource(dataSource);
    itemWriter.setSql(query);
    itemWriter.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider());
    itemWriter.afterPropertiesSet();

    return itemWriter;
  }
}



3. Один из классов, который эту абстракцию реализует (остальные аналогичные):
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
@Slf4j
public class TagBatchImpl extends AbstractBatchImpl<TagDto> {

  public TagBatchImpl(
          DataSource dataSource,
          String fileName
  ) {
    super(dataSource, fileName, TagDto.class);
    setHeaders(new String[]{"id", "name"});
    setQuery(
            "SET IDENTITY_INSERT tags ON; " +
            "INSERT INTO tags (id, name) VALUES (:id, :name);"+
            "SET IDENTITY_INSERT tags OFF;"
    );
  }

}



То-есть по хорошему как минимум создание step'ов хотелось бы засунуть внутрь соответствующего класса, но так-как я не настолько глубоко понимаю, как работает спринга, то это у меня не получается. Все попытки сводились к тому, что вот это:
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
  @Bean
  public Job job() {
    return jobBuilderFactory.get("job")
            .start(tagStep())
            .next(authorStep())
            .next(bookStep())
            .build();
  }


эти step'ы в итоге не видит..
Надеюсь на любые подсказки, спасибо!
...
Рейтинг: 0 / 0
12.12.2021, 20:46
    #40119486
chpasha
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Подскажите как оптимизировать код
bobo96
То-есть по хорошему как минимум создание step'ов хотелось бы засунуть внутрь соответствующего класса
не уверен, что это хорошая идея. Я думаю, что понимаю, откуда растут ноги - вы видимо только начали изучение spring batch и вам приходится писать кучу повторяющегося непонятного кода, который хочеться засунуть куда-то, чтоб не писать каждый раз. Но по сути это не код, а абстракции spring-а для конфигурации типовых задач - стоит ли вводить еще свой уровень абстракции над этим - большой вопрос. Например код для создания CsvReader-а можно записать гораздо компактней, например

Код: java
1.
2.
3.
4.
5.
6.
7.
new FlatFileItemReaderBuilder<>()
				.linesToSkip(5)
				.resource(null)
				.fieldSetMapper(new BeanWrapperFieldSetMapper<>())
				.delimited()
                                .names(xxxx)
				.build()


нужно ли подобный код еще выделять в отдельный метод/класс...ну не знаю, при 3 ридерах я бы этого не делал, но если там реально меняется только список колонок, то можно написать метод для создания ридера, передавая колонки как параметр, но не создавать для этой ерунды отдельный класс - там по сути нет кода, только конфигурация - так пусть она и остается в классе конфигурации. Тоже касается Writer - там точно так же есть JdbcBatchItemWriterBuilder который делать запись компактней

Код: java
1.
2.
3.
4.
5.
new JdbcBatchItemWriterBuilder<>()
						.dataSource()
						.sql()
						.itemSqlParameterSourceProvider()
						.build()


снова таки, заслуживает ли подобный примитив выделения в отдельный код? имхо нет. По-крайней мере не на 3 случаях использования. На 10-20 я бы еще подумал. Рано или поздно логика усложняется и код придется адаптировать под изменяющиеся условия. Тоже касается засовывания создания step-ов в классы.

я бы написал так (для остальных шагов аналогично)
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
@Bean
    public Job job(JobBuilderFactory jobBuilderFactory) {
        return jobBuilderFactory.get("job")
                                .start(tagStep(null))
                                .next(authorStep(null))
                                .next(bookStep(null))
                                .build();
    }

    @Bean
    public Step tagStep(StepBuilderFactory stepBuilderFactory) {
        return stepBuilderFactory.get("tag-step")
                                 .<TagDto, TagDto>chunk(10)
                                 .reader(tagReader(null))
                                 .writer(tagWriter(null, null))
                                 .build();
    }

    @Bean
    public ItemReader<TagDto> tagReader(@Value("${custom.csv.tags.file-name}") String file) {
        return createCsvReader(file, "a", "b", "c");
    }

    @Bean
    public ItemWriter<TagDto> tagWriter(DataSource dataSource, @Value("${custom.csv.tags.sql}") String sql) {
        return new JdbcBatchItemWriterBuilder<TagDto>()
                .dataSource(dataSource)
                .sql(sql)
                .itemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>())
                .build();
    }

    private <T> ItemReader<T> createCsvReader(String file, String...names) {
        return new FlatFileItemReaderBuilder<T>()
                .linesToSkip(5)
                .resource(new FileSystemResource(file))
                .fieldSetMapper(new BeanWrapperFieldSetMapper<>())
                .delimited()
                .names(names)
                .build();
    }


я выделил повторяющийся код в отдельный метод, но это хорошо работает только пока код идентичен - как только у ридеров/врайтеров пойдут свои особые случаи, когда все не ограничивается только набором колонок и sql, оно будет скорее мешать, чем помогать, но в простом примере ... ну пусть будет, по-крайней мере в пределах одного конфига для одного конкретного Job
...
Рейтинг: 0 / 0
12.12.2021, 21:32
    #40119504
mayton
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Подскажите как оптимизировать код
Не оптимизация а скорее рефакторинг.
...
Рейтинг: 0 / 0
Форумы / Java [игнор отключен] [закрыт для гостей] / Подскажите как оптимизировать код / 3 сообщений из 3, страница 1 из 1
Целевая тема:
Создать новую тему:
Автор:
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]