Spring Batch "Nombre de objeto no válido BATCH_JOB_INSTANCE"

Sep 27 2020

Creé un lote de primavera para consultar una base de datos del servidor SQL de Azure y escribir los datos en un archivo CSV. No tengo createpermisos para la base de datos. Recibo este error Invalid Object name BATCH_JOB_INSTANCEal ejecutar el lote. No quiero que las tablas de metadatos por lotes de primavera se creen en la base de datos principal. O sería útil si pudiera tenerlos en otra base de datos local o en memoria como h2db. También he agregado spring-batch-initialize-schema=neverya, que fue el caso con la mayoría de las respuestas a preguntas similares aquí, pero eso no ayudó.

Editar:

Resolví el Invalid Object nameerror evitando que las tablas de metadatos se crearan en la base de datos principal extendiendo la DefaultBatchConfigurerclase y anulando el setDataSourcemétodo, por lo que se crearon en el repositorio de mapas en memoria. Ahora quiero probar dos opciones:

  1. Cómo hacer que las tablas de metadatos se creen en una base de datos local o en una base de datos en memoria como h2db.
  2. O si ya tengo las tablas de metadatos creadas en la base de datos principal, en un esquema diferente al de mi tabla principal de la que estoy obteniendo. Cómo apuntar mi trabajo a esas tablas de metadatos en otro esquema, para almacenar el trabajo y los datos de los detalles del paso en esos.
@Configuration
public class SpringBatchConfig extends DefaultBatchConfigurer{

@Override
    public void setDataSource(DataSource datasource) {

    }
...

Mi archivo application.properties se ve así:

spring.datasource.url=
spring.datasource.username=
spring.datasource.password=
spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver

spring-batch-initialize-schema=never
spring.batch.job.enabled=false

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.SQLServer2012Dialect

Respuestas

MahmoudBenHassine Oct 02 2020 at 07:57

Cómo hacer que las tablas de metadatos se creen en una base de datos local o en una base de datos en memoria como h2db.

Puedes usar spring.batch.initialize-schema=embeddedpara eso.

O si ya tengo las tablas de metadatos creadas en la base de datos principal, en un esquema diferente al de mi tabla principal de la que estoy obteniendo. Cómo apuntar mi trabajo a esas tablas de metadatos en otro esquema, para almacenar el trabajo y los datos de los detalles del paso en esos.

El lote de primavera funciona con una fuente de datos, no con un esquema en particular. Si las tablas de metadatos están en un esquema diferente, entonces necesita crear una segunda fuente de datos que apunte a ese esquema y configurarlo en el repositorio de trabajos.

JosephXu Oct 09 2020 at 06:45

He creado una demostración con dos fuentes de datos. Los metadatos por lotes se almacenarán en H2 DB y la fuente de datos del trabajo es Azure SQL.

Aquí está la estructura del proyecto:

  1. Necesitamos definir una DataSourceConfigclase y usar la @Primaryanotación para el bean DataSource:
@Configuration
public class DataSourceConfig {

    @Bean(name = "mssqlDataSource")
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource appDataSource(){
        return DataSourceBuilder.create().build();
    }
    
    @Bean(name = "h2DataSource")
    @Primary
    // @ConfigurationProperties(prefix="spring.datasource.h2")
    public DataSource h2DataSource() {
        return DataSourceBuilder.create()
                .url("jdbc:h2:mem:thing:H2;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE")
                .driverClassName("org.h2.Driver")
                .username("sa")
                .password("")                   
                .build();
    }
    
}
  1. En la clase ItemReaderDbDemo, usamos @Autowired @Qualifier("mssqlDataSource")para especificar el origen de datos en la tarea de Spring Batch:
@Configuration
public class ItemReaderDbDemo {
    
    //generate task Object
    @Autowired
    private JobBuilderFactory jobBuilderFactory;
    
    //Step exec tasks
    //generate step Object
    @Autowired
    private StepBuilderFactory stepBuilderFactory;

    @Autowired
    @Qualifier("mssqlDataSource")
    private DataSource dataSource;

    @Autowired
    @Qualifier("dbJdbcWriter")
    private ItemWriter<? super Todo> dbJdbcWriter;
    
    @Bean
    public Job itemReaderDbDemoJob() {
        return jobBuilderFactory.get("itemReaderDbDemoJob").start(itemReaderDbStep()).build();
    }

    @Bean
    public Step itemReaderDbStep() {
        return stepBuilderFactory.get("itemReaderDbStep")
                .<Todo,Todo>chunk(2)
                .reader(dbJdbcReader())
                .writer(dbJdbcWriter)
                .build();
    }

    @Bean
    @StepScope
    public JdbcPagingItemReader<Todo> dbJdbcReader() {
        JdbcPagingItemReader<Todo> reader = new JdbcPagingItemReader<Todo>();
        reader.setDataSource(dataSource);
        reader.setFetchSize(2);
        reader.setRowMapper(new  RowMapper<Todo>() {
            @Override
            public Todo mapRow(ResultSet rs, int rowNum) throws SQLException {
                Todo todo = new Todo();
                todo.setId(rs.getLong(1));
                todo.setDescription(rs.getString(2));
                todo.setDetails(rs.getString(3));
                return todo;
            }   
            
        });
        SqlServerPagingQueryProvider provider = new  SqlServerPagingQueryProvider();
        provider.setSelectClause("id,description,details");
        provider.setFromClause("from dbo.todo");
        
        //sort
        Map<String,Order> sort = new HashMap<>(1);
        sort.put("id", Order.DESCENDING);
        provider.setSortKeys(sort);
        
        reader.setQueryProvider(provider);
        return reader;
    }
}

  1. Aquí está mi application.properties:
logging.level.org.springframework.jdbc.core=DEBUG

spring.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.datasource.jdbcUrl=jdbc:sqlserver://josephserver2.database.windows.net:1433;database=<Your-Database-Name>;encrypt=true;trustServerCertificate=false;hostNameInCertificate=*.database.windows.net;loginTimeout=30;
spring.datasource.username=<Your-UserName>
spring.datasource.password=<Your-Password>
spring.datasource.initialization-mode=always
  1. Devuelve el resultado esperado de mi Azure SQL. Por cierto, mi nombre de usuario SQL de Azure no tiene permisos de creación para la base de datos.
    El resultado muestra: