Spring Batch “Nome de objeto inválido BATCH_JOB_INSTANCE”

Sep 27 2020

Criei um lote de primavera para consultar um banco de dados do servidor SQL do Azure e gravar os dados em um arquivo CSV. Não tenho createpermissão para o banco de dados. Recebo este erro Invalid Object name BATCH_JOB_INSTANCEao executar o lote. Não quero que as tabelas de metadados de lote do Spring sejam criadas no banco de dados principal. Ou seria útil se eu pudesse tê-los em outro banco de dados local ou na memória como o h2db. Também spring-batch-initialize-schema=neverjá adicionei , que era o caso da maioria das respostas a perguntas semelhantes aqui, mas isso não ajudou.

Editar:

Resolvi o Invalid Object nameerro evitando que as tabelas de metadados fossem criadas no banco de dados principal estendendo a DefaultBatchConfigurerclasse e substituindo o setDataSourcemétodo, fazendo com que fossem criadas no repositório de mapas na memória. Agora, quero tentar duas opções:

  1. Como fazer com que as tabelas de metadados sejam criadas em um banco de dados local ou em memória como o h2db.
  2. Ou Se eu já tiver as tabelas de metadados criadas no banco de dados principal, em um esquema diferente do da minha tabela principal que estou buscando. Como apontar meu trabalho para essas tabelas de metadados em outro esquema, para armazenar os dados de detalhes do trabalho e da etapa neles.
@Configuration
public class SpringBatchConfig extends DefaultBatchConfigurer{

@Override
    public void setDataSource(DataSource datasource) {

    }
...

Meu arquivo application.properties tem a seguinte aparência:

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

Respostas

MahmoudBenHassine Oct 02 2020 at 07:57

Como fazer com que as tabelas de metadados sejam criadas em um banco de dados local ou em memória como o h2db.

Você pode usar spring.batch.initialize-schema=embeddedpara isso.

Ou Se eu já tiver as tabelas de metadados criadas no banco de dados principal, em um esquema diferente do da minha tabela principal que estou buscando. Como apontar meu trabalho para essas tabelas de metadados em outro esquema, para armazenar os dados de detalhes do trabalho e da etapa neles.

spring batch funciona em uma fonte de dados, não em um esquema específico. Se as tabelas de metadados estiverem em um esquema diferente, você precisará criar uma segunda fonte de dados apontando para esse esquema e configurá-lo no repositório de tarefas.

JosephXu Oct 09 2020 at 06:45

Eu criei uma demonstração com duas fontes de dados. Os metadados em lote serão armazenados no banco de dados H2 e a fonte de dados do trabalho é o Azure SQL.

Aqui está a estrutura do projeto:

  1. Precisamos definir uma DataSourceConfigclasse e usar @Primaryanotações para o 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. Na classe ItemReaderDbDemo, usamos @Autowired @Qualifier("mssqlDataSource")para especificar o dataSource na tarefa 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. Aqui está meu 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. Ele retorna o resultado esperado do meu Azure SQL. A propósito, meu nome de usuário SQL do Azure não tem permissões de criação para o banco de dados.
    O resultado mostra: