Spring Batch - JpaPagingItemReader - Работает в MySQL - Дубликаты в PostgreSQL

0

Перед запуском Spring Batch. У меня есть таблица импорта, которая содержит все элементы, которые необходимо импортировать в нашу систему. Именно в этот момент проверено, что он содержит только те элементы, которые не существуют в нашей системе.

Затем у меня есть Spring Batch Job, которое читается из этой таблицы импорта с помощью JpaPagingItemReader. После выполнения работы он записывает db, используя ItemWriter.

Я запускаю с размером страницы и размером порции на 10000. Теперь это работает отлично, когда работает на MySQL innoDB. Я могу даже использовать несколько потоков, и все работает нормально.

Но теперь мы переходим к PostgreSQL, и одно и то же пакетное задание запускается в очень странную проблему. Что происходит, так это то, что он пытается вставить дубликаты в нашу систему. Естественно, это будет отвергаться с помощью уникальных ограничений индекса и возникает ошибка. Поскольку таблица импорта db проверяется на наличие только несуществующих перед запуском пакетного задания, единственной причиной этого я могу подумать, что JpaPagingItemReader читает несколько строк несколько раз из таблицы импорта db, когда я запускаю Postgres. Но зачем это делать?

Я экспериментировал с множеством настроек. Поворот фрагмента и размера страницы до примерно 100 приводит к тому, что импорт медленнее, но по-прежнему такой же. Выполнение однопоточного, а не нескольких потоков приводит к тому, что ошибка происходит чуть позже. Так что же может быть причиной того, что мой JpaPagingItemReader читает одни и те же элементы несколько раз только на PostgresSQL? Оператор select, поддерживающий читателя, прост, его NamedQuery:

@NamedQuery(name = "ImportDTO.findAllForInsert",
            query = "select h from ImportDTO h where h.toBeImported = true")

Также обратите внимание, что флажок toBeImported не будет изменен пакетным заданием вообще во время выполнения, поэтому результаты этого запроса должны всегда возвращаться так же, как до, так и после пакетного задания.

Любые идеи, советы или помощь очень полезны!

Вот код Batch Config:

import javax.persistence.EntityManagerFactory;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.launch.support.SimpleJobLauncher;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.item.database.JpaPagingItemReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;

@Configuration
@EnableBatchProcessing
public class BatchConfiguration {

    @Autowired
    private JobBuilderFactory jobBuilderFactory;
    @Autowired
    private StepBuilderFactory stepBuilderFactory;
    @Autowired
    private OrganizationItemWriter organizationItemWriter;
    @Autowired
    private EntityManagerFactory entityManagerFactory;
    @Autowired
    private OrganizationUpdateProcessor organizationUpdateProcessor;
    @Autowired
    private OrganizationInsertProcessor organizationInsertProcessor;

    private Integer organizationBatchSize = 10000;
    private Integer organizationThreadSize = 3;
    private Integer maxThreadSize = organizationThreadSize;

    @Bean
    public SimpleJobLauncher jobLauncher(JobRepository jobRepository) {
        SimpleJobLauncher launcher = new SimpleJobLauncher();
        launcher.setJobRepository(jobRepository);
        return launcher;
    }

    @Bean
    public JpaPagingItemReader<ImportDTO> findNewImportsToImport() throws Exception {
        JpaPagingItemReader<ImportDTO> databaseReader = new JpaPagingItemReader<>();
        databaseReader.setEntityManagerFactory(entityManagerFactory);
        JpaQueryProviderImpl<ImportDTO> jpaQueryProvider = new JpaQueryProviderImpl<>();
        jpaQueryProvider.setQuery("ImportDTO.findAllForInsert");
        databaseReader.setQueryProvider(jpaQueryProvider);
        databaseReader.setPageSize(organizationBatchSize);
        // must be set to false if multi threaded
        databaseReader.setSaveState(false);
        databaseReader.afterPropertiesSet();
        return databaseReader;
    }

    @Bean
    public JpaPagingItemReader<ImportDTO> findImportsToUpdate() throws Exception {
        JpaPagingItemReader<ImportDTO> databaseReader = new JpaPagingItemReader<>();
        databaseReader.setEntityManagerFactory(entityManagerFactory);
        JpaQueryProviderImpl<ImportDTO> jpaQueryProvider = new JpaQueryProviderImpl<>();
        jpaQueryProvider.setQuery("ImportDTO.findAllForUpdate");
        databaseReader.setQueryProvider(jpaQueryProvider);
        databaseReader.setPageSize(organizationBatchSize);
        // must be set to false if multi threaded
        databaseReader.setSaveState(false);
        databaseReader.afterPropertiesSet();
        return databaseReader;
    }

    @Bean
    public OrganizationItemWriter writer() throws Exception {
        return organizationItemWriter;
    }

    @Bean
    public StepExecutionNotificationListener stepExecutionListener() {
        return new StepExecutionNotificationListener();
    }

    @Bean
    public ChunkExecutionListener chunkListener() {
        return new ChunkExecutionListener();
    }

    @Bean
    public TaskExecutor taskExecutor() {
        SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
        taskExecutor.setConcurrencyLimit(maxThreadSize);
        return taskExecutor;
    }

    @Bean
    public Job importOrganizationsJob(JobCompletionNotificationListener listener) throws Exception {
        return jobBuilderFactory.get("importAndUpdateOrganizationJob")
                .incrementer(new RunIdIncrementer())
                .listener(listener)
                .start(importNewOrganizationsFromImports())
                .next(updateOrganizationsFromImports())
                .build();
    }

    @Bean
    public Step importNewOrganizationsFromImports() throws Exception {
        return stepBuilderFactory.get("importNewOrganizationsFromImports")
                .<ImportDTO, Organization> chunk(organizationBatchSize)
                .reader(findNewImportsToImport())
                .processor(organizationInsertProcessor)
                .writer(writer())
                .taskExecutor(taskExecutor())
                .listener(stepExecutionListener())
                .listener(chunkListener())
                .throttleLimit(organizationThreadSize)
                .build();
    }


    @Bean
    public Step updateOrganizationsFromImports() throws Exception {
        return stepBuilderFactory.get("updateOrganizationsFromImports")
                .<ImportDTO, Organization> chunk(organizationBatchSize)
                .reader(findImportsToUpdate())
                .processor(organizationUpdateProcessor)
                .writer(writer())
                .taskExecutor(taskExecutor())
                .listener(stepExecutionListener())
                .listener(chunkListener())
                .throttleLimit(organizationThreadSize)
                .build();
    }
}
  • 0
    Как создаются ключи? Они принесены или произведены новые?
  • 0
    Ключи генерируются новыми. Проблема не в ключах. это уникальный идентификатор ImportDTO. Я получил уникальный индекс по нему как в таблице импорта, так и в таблице назначения
Теги:
jpa
spring-batch

1 ответ

0
Лучший ответ

Вам нужно добавить опцию order by, чтобы выбрать

  • 0
    Большое спасибо. Это было фактически все, что было нужно. Кажется, что MySQL всегда возвращал один и тот же порядок, хотя я не указывал порядок по. В то время как PostgreSQL вернул более случайный порядок без порядка.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню