Search code examples
sql-serverspringstored-proceduresspring-batchspring-jdbc

spring batch: efficient way to query results of a stored procedure within a tasklet


My goal is to query the results of a stored procedure (sql server) using a custom row mapper within a spring batch tasklet.

I am using Spring Boot (version 2.2.2.RELEASE) and Spring Batch (4.2.1.RELEASE).

Example:

Stored Procedure:

CREATE PROCEDURE storedProcName @numbers VARCHAR(max), @day DATE
AS
SET NOCOUNT ON;

SELECT something, something2, something3
FROM sometable
WHERE ids in (select value from string_split(@numbers,','))
AND day = @day

Custom Row Mapper:

public class CustomRowMapper implements RowMapper<CustomObject> {
    private static final String SOMETHING = "something";
    private static final String SOMETHING2 = "something2";
    private static final String SOMETHING3 = "something3";

    @Override
    public CustomObject mapRow(ResultSet resultSet, int i) throws SQLException {
        CustomObject customObject = new CustomObject();
        customObject.setSomething(resultSet.getString(SOMETHING));
        customObject.setSomething2(resultSet.getString(SOMETHING2));
        customObject.setSomething3(resultSet.getInt(SOMETHING3));

        return customObject;
    }
}

Execute Stored Procedure and query the results:

SimpleJdbcCall jdbcCall = new SimpleJdbcCall(jdbcTemplate)
        .withProcedureName("storedProcName")
        .returningResultSet("test", new CustomRowMapper());
Map<String, Object> out = jdbcCall.execute(parameterSource);
List<CustomObject> customObjects = (List<CustomObject>) out.get("test");

It works fine, but there has to be a more efficient way to do this? But i didn't found anything useful.


Solution

  • there has to be a more efficient way to do this

    Spring Batch provides the StoredProcedureItemReader which can call a given stored procedure and iterate over its results. It also allows you to use a custom mapper as the one you defined.

    According to your code snippet, you call the procedure and get a List<CustomObject> over which you will need to iterate at some point. So I think a chunk oriented tasklet with a StoredProcedureItemReader<CustomObject> is a good choice for you:

    @Bean
    public StoredProcedureItemReader<CustomObject> itemReader() {
        return new StoredProcedureItemReaderBuilder<CustomObject>()
                .procedureName("storedProcName")
                .rowMapper(new CustomRowMapper())
                // set other properties
                .build();
    }
    

    Edit: show how to return a synchronized reader

    @Bean
    @StepScope
    public SynchronizedItemStreamReader<CustomObject> itemReader() {
        StoredProcedureItemReader<CustomObject> reader = new StoredProcedureItemReaderBuilder<>()
                .procedureName("storedProcName")
                .rowMapper(new CustomRowMapper())
                // set other properties
                .build();
    
        SynchronizedItemStreamReader<CustomObject> synchronizedReader = new SynchronizedItemStreamReader<>();
        synchronizedReader.setDelegate(reader);
        return synchronizedReader;
    }