Spring Batch "Nombre de objeto no válido BATCH_JOB_INSTANCE"
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:
- 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.
- 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
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.
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:
- 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();
}
}
- 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;
}
}
- 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
- 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: