tutorial sirve requestmapping que proyecto para mvc ejemplo crear basico spring spring-batch spring-boot spring-java-config

spring - sirve - ¿Cómo configurar Java en fuentes de datos separadas para datos de lotes de primavera y datos de negocios? ¿Debería incluso hacerlo?



spring mvc (5)

¿Has probado algo como esto ya?

@Bean(name="batchDataSource") public DataSource batchDataSource(){ return DataSourceBuilder.create() .url(env.getProperty("batchdb.url")) .driverClassName(env.getProperty("batchdb.driver")) .username(env.getProperty("batchdb.username")) .password(env.getProperty("batchdb.password")) .build(); }

y luego marque la otra fuente de datos con un @Primary, y use un @Qualifier en su configuración por lotes para especificar que desea conectar automáticamente el bean batchDataSource.

Mi trabajo principal solo lee operaciones y el otro escribe algo, pero en el MyISAM engine que ignora las transacciones, por lo que no requeriría necesariamente soporte de transacciones. ¿Cómo puedo configurar Spring Batch para que tenga su propia JobRepository datos para JobRepository , separada de la que contiene los datos comerciales? La configuración inicial de una fuente de datos se realiza de la siguiente manera:

@Configuration public class StandaloneInfrastructureConfiguration { @Autowired Environment env; @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory() { LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(dataSource()); em.setPackagesToScan(new String[] { "org.podcastpedia.batch.*" }); JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); em.setJpaVendorAdapter(vendorAdapter); em.setJpaProperties(additionalJpaProperties()); return em; } Properties additionalJpaProperties() { Properties properties = new Properties(); properties.setProperty("hibernate.hbm2ddl.auto", "none"); properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect"); properties.setProperty("hibernate.show_sql", "true"); return properties; } @Bean public DataSource dataSource(){ return DataSourceBuilder.create() .url(env.getProperty("db.url")) .driverClassName(env.getProperty("db.driver")) .username(env.getProperty("db.username")) .password(env.getProperty("db.password")) .build(); } @Bean public PlatformTransactionManager transactionManager(EntityManagerFactory emf){ JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(emf); return transactionManager; } }

y luego se importa en la clase de configuración del Job donde la anotación @EnableBatchProcessing la @EnableBatchProcessing . Mi idea inicial fue intentar establecer la clase de configuración extendiendo el DefaultBatchConfigurer , pero luego obtengo una

BeanCurrentlyInCreationException (org.springframework.beans.factory.BeanCurrentlyInCreationException: Error al crear el bean con el nombre jobBuilders: el bean solicitado está actualmente en creación: ¿hay una referencia circular que no se pueda resolver?):

@Configuration @EnableBatchProcessing @Import({StandaloneInfrastructureConfiguration.class, NotifySubscribersServicesConfiguration.class}) public class NotifySubscribersJobConfiguration extends DefaultBatchConfigurer { @Autowired private JobBuilderFactory jobBuilders; @Autowired private StepBuilderFactory stepBuilders; @Autowired private DataSource dataSource; @Autowired Environment env; @Override @Autowired public void setDataSource(javax.sql.DataSource dataSource) { super.setDataSource(batchDataSource()); } private DataSource batchDataSource(){ return DataSourceBuilder.create() .url(env.getProperty("batchdb.url")) .driverClassName(env.getProperty("batchdb.driver")) .username(env.getProperty("batchdb.username")) .password(env.getProperty("batchdb.password")) .build(); } @Bean public ItemReader<User> notifySubscribersReader(){ JdbcCursorItemReader<User> reader = new JdbcCursorItemReader<User>(); String sql = "select * from users where is_email_subscriber is not null"; reader.setSql(sql); reader.setDataSource(dataSource); reader.setRowMapper(rowMapper()); return reader; } ........ }

Cualquier pensamiento es más que bienvenido. El proyecto está disponible en GitHub - https://github.com/podcastpedia/podcastpedia-batch

Gracias un montón.


Ok, esto es extraño pero funciona. Mover las fuentes de datos a su propia clase de configuración funciona bien y uno es capaz de autowire.

El ejemplo es una versión de fuente de datos múltiple de Spring Batch Service Example :

DataSourceConfiguration :

public class DataSourceConfiguration { @Value("classpath:schema-mysql.sql") private Resource schemaScript; @Bean @Primary public DataSource hsqldbDataSource() throws SQLException { final SimpleDriverDataSource dataSource = new SimpleDriverDataSource(); dataSource.setDriver(new org.hsqldb.jdbcDriver()); dataSource.setUrl("jdbc:hsqldb:mem:mydb"); dataSource.setUsername("sa"); dataSource.setPassword(""); return dataSource; } @Bean public JdbcTemplate jdbcTemplate(final DataSource dataSource) { return new JdbcTemplate(dataSource); } @Bean public DataSource mysqlDataSource() throws SQLException { final SimpleDriverDataSource dataSource = new SimpleDriverDataSource(); dataSource.setDriver(new com.mysql.jdbc.Driver()); dataSource.setUrl("jdbc:mysql://localhost/spring_batch_example"); dataSource.setUsername("test"); dataSource.setPassword("test"); DatabasePopulatorUtils.execute(databasePopulator(), dataSource); return dataSource; } @Bean public JdbcTemplate mysqlJdbcTemplate(@Qualifier("mysqlDataSource") final DataSource dataSource) { return new JdbcTemplate(dataSource); } private DatabasePopulator databasePopulator() { final ResourceDatabasePopulator populator = new ResourceDatabasePopulator(); populator.addScript(schemaScript); return populator; } }

Configuración de lotes :

@Configuration @EnableBatchProcessing @Import({ DataSourceConfiguration.class, MBeanExporterConfig.class }) public class BatchConfiguration { @Autowired private JobBuilderFactory jobs; @Autowired private StepBuilderFactory steps; @Bean public ItemReader<Person> reader() { final FlatFileItemReader<Person> reader = new FlatFileItemReader<Person>(); reader.setResource(new ClassPathResource("sample-data.csv")); reader.setLineMapper(new DefaultLineMapper<Person>() { { setLineTokenizer(new DelimitedLineTokenizer() { { setNames(new String[] { "firstName", "lastName" }); } }); setFieldSetMapper(new BeanWrapperFieldSetMapper<Person>() { { setTargetType(Person.class); } }); } }); return reader; } @Bean public ItemProcessor<Person, Person> processor() { return new PersonItemProcessor(); } @Bean public ItemWriter<Person> writer(@Qualifier("mysqlDataSource") final DataSource dataSource) { final JdbcBatchItemWriter<Person> writer = new JdbcBatchItemWriter<Person>(); writer.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<Person>()); writer.setSql("INSERT INTO people (first_name, last_name) VALUES (:firstName, :lastName)"); writer.setDataSource(dataSource); return writer; } @Bean public Job importUserJob(final Step s1) { return jobs.get("importUserJob").incrementer(new RunIdIncrementer()).flow(s1).end().build(); } @Bean public Step step1(final ItemReader<Person> reader, final ItemWriter<Person> writer, final ItemProcessor<Person, Person> processor) { return steps.get("step1") .<Person, Person> chunk(1) .reader(reader) .processor(processor) .writer(writer) .build(); } }


Por docs.spring.io/spring-boot/docs/current/reference/htmlsingle/… :

@Bean @Primary @ConfigurationProperties("app.datasource.first") public DataSourceProperties firstDataSourceProperties() { return new DataSourceProperties(); } @Bean @Primary @ConfigurationProperties("app.datasource.first") public DataSource firstDataSource() { return firstDataSourceProperties().initializeDataSourceBuilder().build(); } @Bean @ConfigurationProperties("app.datasource.second") public DataSourceProperties secondDataSourceProperties() { return new DataSourceProperties(); } @Bean @ConfigurationProperties("app.datasource.second") public DataSource secondDataSource() { return secondDataSourceProperties().initializeDataSourceBuilder().build(); }

En las propiedades de la aplicación, puede usar las propiedades regulares del origen de datos:

app.datasource.first.type=com.zaxxer.hikari.HikariDataSource app.datasource.first.maximum-pool-size=30 app.datasource.second.url=jdbc:mysql://localhost/test app.datasource.second.username=dbuser app.datasource.second.password=dbpass app.datasource.second.max-total=30


Suponiendo que tiene 2 orígenes de datos, uno para metadatos de lotes de resortes como los detalles del trabajo [digamos CONFIGDB] y otro para los datos de su negocio [digamos AppDB]:

Inyecte CONFIGDB en jobRepository, así:

<bean id="jobRepository" class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean"> <property name="transactionManager" ref="transactionManager" /> <property name="dataSource" ref="CONFIGDB" /> <property name="databaseType" value="db2" /> <property name="tablePrefix" value="CONFIGDB.BATCH_" /> </bean>

Ahora puede inyectar el dartasource de AppDB en los escritores OR de su DAO, si los hay.

<bean id="DemoItemWriter" class="com.demoItemWriter"> <property name="dataSource" ref="AppDB" /> </bean>

O

puede definir un recurso e inyectar este AppDB con jndi searchup en la clase donde se necesita:

public class ExampleDAO { @Resource(lookup = "java:comp/env/jdbc/AppDB") DataSource ds;

}


Tengo mis fuentes de datos en una clase de configuración separada. En la configuración por lotes, extendemos DefaultBatchConfigurer y reemplazamos el método setDataSource , pasando la base de datos específica para usar con Spring Batch con un @Qualifier. No pude hacer que esto funcionara con la versión del constructor, pero el método de establecimiento funcionó para mí.

My Reader, Processor y Writer''s están en sus propias clases independientes, junto con los pasos.

Esto está utilizando Spring Boot 1.1.8 y Spring Batch 3.0.1. Nota: tuvimos una configuración diferente para un proyecto con Spring Boot 1.1.5 que no funcionó igual en la versión más reciente.

package org.sample.config.jdbc; import javax.sql.DataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.env.Environment; import com.atomikos.jdbc.AtomikosDataSourceBean; import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource; /** * The Class DataSourceConfiguration. * */ @Configuration public class DataSourceConfig { private final static Logger log = LoggerFactory.getLogger(DataSourceConfig.class); @Autowired private Environment env; /** * Siphon data source. * * @return the data source */ @Bean(name = "mainDataSource") @Primary public DataSource mainDataSource() { final String user = this.env.getProperty("db.main.username"); final String password = this.env.getProperty("db.main.password"); final String url = this.env.getProperty("db.main.url"); return this.getMysqlXADataSource(url, user, password); } /** * Batch data source. * * @return the data source */ @Bean(name = "batchDataSource", initMethod = "init", destroyMethod = "close") public DataSource batchDataSource() { final String user = this.env.getProperty("db.batch.username"); final String password = this.env.getProperty("db.batch.password"); final String url = this.env.getProperty("db.batch.url"); return this.getAtomikosDataSource("metaDataSource", this.getMysqlXADataSource(url, user, password)); } /** * Gets the mysql xa data source. * * @param url the url * @param user the user * @param password the password * @return the mysql xa data source */ private MysqlXADataSource getMysqlXADataSource(final String url, final String user, final String password) { final MysqlXADataSource mysql = new MysqlXADataSource(); mysql.setUser(user); mysql.setPassword(password); mysql.setUrl(url); mysql.setPinGlobalTxToPhysicalConnection(true); return mysql; } /** * Gets the atomikos data source. * * @param resourceName the resource name * @param xaDataSource the xa data source * @return the atomikos data source */ private AtomikosDataSourceBean getAtomikosDataSource(final String resourceName, final MysqlXADataSource xaDataSource) { final AtomikosDataSourceBean atomikos = new AtomikosDataSourceBean(); atomikos.setUniqueResourceName(resourceName); atomikos.setXaDataSource(xaDataSource); atomikos.setMaxLifetime(3600); atomikos.setMinPoolSize(2); atomikos.setMaxPoolSize(10); return atomikos; } } package org.sample.settlement.batch; import javax.sql.DataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.batch.core.Job; import org.springframework.batch.core.Step; import org.springframework.batch.core.configuration.annotation.DefaultBatchConfigurer; 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.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.PlatformTransactionManager; /** * The Class BatchConfiguration. * */ @Configuration @EnableBatchProcessing public class BatchConfiguration extends DefaultBatchConfigurer { private final static Logger log = LoggerFactory.getLogger(BatchConfiguration.class); @Autowired private JobBuilderFactory jobs; @Autowired private StepBuilderFactory steps; @Autowired private PlatformTransactionManager transactionManager; @Autowired @Qualifier("processStep") private Step processStep; /** * Process payments job. * * @return the job */ @Bean(name = "processJob") public Job processJob() { return this.jobs.get("processJob") .incrementer(new RunIdIncrementer()) .start(processStep) .build(); } @Override @Autowired public void setDataSource(@Qualifier("batchDataSource") DataSource batchDataSource) { super.setDataSource(batchDataSource); } }