#spring-boot #spring-batch
#пружинный ботинок #пружинная партия
Вопрос:
У меня есть 2 источника данных JDBC, определенных в приложении Spring Boot, которые используются в пакетном задании Spring. Однако после автоматического подключения источников данных используется только один. Используется тот, который помечен как @Primary. Если я помещу аннотацию в другой источник данных JDBC, который будет использоваться вместо этого. В двух словах, только один из источников данных JDBC когда-либо используется. Я использую Ломбок в некоторых местах, но я не уверен, играет ли это какую-то роль.
Вот источники данных:
application.yml symphony: datasource: driver-class-name: oracle.jdbc.OracleDriver url: ... type: com.zaxxer.hikari.HikariDataSource username: lt;USRgt; password: lt;PWDgt; jndi-name: false repo: datasource: driver-class-name: org.h2.Driver url: jdbc:h2:mem:db;DB_CLOSE_DELAY=-1 username: sa password: sa jndi-name: false
Вот первый источник данных:
@Configuration public class RepoDbConfig { @Bean @ConfigurationProperties("repo.datasource") public DataSourceProperties repoDataProperties() { return new DataSourceProperties(); } @Bean(name = "repoDataSource") public DataSource dataSourcerepo() { DataSource dataSource = repoDataProperties().initializeDataSourceBuilder().type(BasicDataSource.class) .build(); return dataSource; } @Bean(name = "repoJdbcTemplate") public JdbcTemplate repoJdbcTemplate(DataSource repoDataSource) { return new JdbcTemplate(repoDataSource); } }
Вот второй источник данных:
@Configuration public class SymphonyDbConfig { @Primary @Bean @ConfigurationProperties("symphony.datasource") public DataSourceProperties symphonyDataSourceProperties() { return new DataSourceProperties(); } @Primary @Bean(name = "symphonyDataSource") public DataSource dataSourcesymphony() { HikariDataSource dataSource = symphonyDataSourceProperties().initializeDataSourceBuilder().type(HikariDataSource.class) .build(); return dataSource; } @Primary @Bean(name = "symphonyJdbcTemplate") public JdbcTemplate symphonyJdbcTemplate(DataSource symphonyDataSource) { return new JdbcTemplate(symphonyDataSource); } }
The JobRepository beans are configured like this:
@Configuration @RequiredArgsConstructor public class JobRepositoryConfig { final @Qualifier("repoDataSource") DataSource repoDataSource; @Bean("repoTransactionManager") AbstractPlatformTransactionManager repoTransactionManager() { return new ResourcelessTransactionManager(); } @Bean("repoJobRepository") public JobRepository repoJobRepository(DataSource repoDataSource) throws Exception { JobRepositoryFactoryBean jobRepositoryFactoryBean = new JobRepositoryFactoryBean(); jobRepositoryFactoryBean.setDataSource(repoDataSource); jobRepositoryFactoryBean.setTransactionManager(repoTransactionManager()); jobRepositoryFactoryBean.setDatabaseType(DatabaseType.H2.getProductName()); return jobRepositoryFactoryBean.getObject(); } @Bean("repoAppJobLauncher") public JobLauncher careLocationAppJobLauncher(JobRepository repoJobRepository) { SimpleJobLauncher simpleJobLauncher = new SimpleJobLauncher(); simpleJobLauncher.setJobRepository(repoJobRepository); return simpleJobLauncher; } }
Finally the Batch Job beans used for the Job are configured here: The only part not shown is the launching of the job. All the required beans used are shown here:
@Configuration @EnableBatchProcessing @EnableScheduling @RequiredArgsConstructor @Slf4j public class CellBatchConfig { private final JobBuilderFactory jobBuilderFactory; @Qualifier("repoAppJobLauncher") private final JobLauncher repoAppJobLauncher; private final StepBuilderFactory stepBuilderFactory; @Value("${chunk-size}") private int chunkSize; @Qualifier("symphonyDataSource") final DataSource symphonyDataSource; @Qualifier("repoDataSource") final DataSource symphonyDataSource; @Bean public JdbcPagingItemReaderlt;CenterDtogt; cellItemReader(PagingQueryProvider pagingQueryProvider) { return new JdbcPagingItemReaderBuilderlt;CenterDtogt;() .name("cellItemReader") .dataSource(symphonyDataSource) .queryProvider(pagingQueryProvider) .pageSize(chunkSize) .rowMapper(new CellRowMapper()) .build(); } @Bean public PagingQueryProvider pagingQueryProvider() { OraclePagingQueryProvider pagingQueryProvider = new OraclePagingQueryProvider(); final Maplt;String, Ordergt; sortKeys = new HashMaplt;gt;(); sortKeys.put("ID", Order.ASCENDING); pagingQueryProvider.setSortKeys(sortKeys); pagingQueryProvider.setSelectClause(" ID, CELL_NO, MAT_VO "); pagingQueryProvider.setFromClause(" from pvc.cells"); return pagingQueryProvider; } ....... }
Ошибка возникает в результате использования только одного из источников данных. Это приводит к тому, что используется для запроса репозитория пакетных заданий Spring, что приводит к его сбою: вот ключевая часть трассировки стека. Он пытается использовать источник данных oracle для запроса ресурсов JobRespository и в результате терпит неудачу:
Caused by: org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [SELECT JOB_INSTANCE_ID, JOB_NAME from BATCH_JOB_INSTANCE where JOB_NAME = ? and JOB_KEY = ?]; nested exception is java.sql.SQLSyntaxErrorException: ORA-00942: table or view does not exist
Ответ №1:
В классе JobRepositoryConfig: В компоненте:
@Bean("symphonyJobRepository") public JobRepository symphonyJobRepository(DataSource repoDataSource) throws Exception { JobRepositoryFactoryBean jobRepositoryFactoryBean = new JobRepositoryFactoryBean(); jobRepositoryFactoryBean.setDataSource(repoDataSource); jobRepositoryFactoryBean.setTransactionManager(repoTransactionManager()); jobRepositoryFactoryBean.setDatabaseType(DatabaseType.H2.getProductName()); return jobRepositoryFactoryBean.getObject(); }
Вы не использовали переменную:
final @Qualifier("repoDataSource") DataSource repoDataSource;
Таким образом, Spring использует объект источника данных, который снабжен аннотацией @Primary
Комментарии:
1. Это не исправило ситуацию , смотрите правки
Ответ №2:
Я исправил это, сделав один боб основным, а также добавив квалификаторы для конкретных бобов, которые отсутствовали, что было упущением с моей стороны. Например, здесь я добавил квалификатор @:
@Primary @Bean(name = "symphonyDataSource") @Qualifier("symphonyDataSource") // This was missing public DataSource dataSourcesymphony() { HikariDataSource dataSource = symphonyDataSourceProperties().initializeDataSourceBuilder().type(HikariDataSource.class) .build(); return dataSource; }