The Spring Batch JdbcCursorItemReader can accept a preparedStatementSetter:
<bean id="reader" class="org.springframework.batch.item.database.JdbcCursorItemReader">
<property name="dataSource" ref="..." />
<property name="sql" value="SELECT * FROM test WHERE col1 = ?">
<property name="rowMapper" ref="..." />
<property name="preparedStatementSetter" ref="..." />
</bean>
This works well if the sql uses ? as placeholder(s), as in the above example. However, our pre-existing sql uses named parameters, e.g. SELECT * FROM test WHERE col1 = :param .
Is there a way to get a JdbcCursorItemReader to work with a NamedPreparedStatementSetter rather than a simple PreparedStatementSetter?
Thanks
You can try with jobParameters
. In this case you don't need any PreparedStatementSetter
.
<bean id="reader" class="org.springframework.batch.item.database.JdbcCursorItemReader">
<property name="dataSource" ref="..." />
<property name="sql" value="SELECT * FROM test WHERE col1 = #{jobParameters['col1']">
<property name="rowMapper" ref="..." />
<property name="preparedStatementSetter" ref="..." />
</bean>
pass the value when running the job
JobParameters param = new JobParametersBuilder().addString("col1", "value1").toJobParameters();
JobExecution execution = jobLauncher.run(job, param);
Once we don't have an official solution from spring, we can fix this problem using a simple approach:
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
public interface SqlParameterSourceProvider {
SqlParameterSource getSqlParameterSource();
}
import org.springframework.batch.item.database.JdbcCursorItemReader;
import org.springframework.jdbc.core.SqlTypeValue;
import org.springframework.jdbc.core.StatementCreatorUtils;
import org.springframework.jdbc.core.namedparam.*;
import org.springframework.util.Assert;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.*;
public class NamedParameterJdbcCursorItemReader<T> extends JdbcCursorItemReader<T> {
private SqlParameterSourceProvider parameterSourceProvider;
private String paramedSql;
public NamedParameterJdbcCursorItemReader(SqlParameterSourceProvider parameterSourceProvider) {
this.parameterSourceProvider = parameterSourceProvider;
}
@Override
public void setSql(String sql) {
Assert.notNull(parameterSourceProvider, "You have to set parameterSourceProvider before the SQL statement");
Assert.notNull(sql, "sql must not be null");
paramedSql = sql;
super.setSql(NamedParameterUtils.substituteNamedParameters(sql, parameterSourceProvider.getSqlParameterSource()));
}
@Override
protected void applyStatementSettings(PreparedStatement stmt) throws SQLException {
final ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(paramedSql);
final List<?> parameters = Arrays.asList(NamedParameterUtils.buildValueArray(parsedSql, parameterSourceProvider.getSqlParameterSource(), null));
for (int i = 0; i < parameters.size(); i++) {
StatementCreatorUtils.setParameterValue(stmt, i + 1, SqlTypeValue.TYPE_UNKNOWN, parameters.get(i));
}
}
}
public class MyCustomSqlParameterSourceProvider implements SqlParameterSourceProvider {
private Map<String, Object> params;
public void updateParams(Map<String, Object> params) {
this.params = params;
}
@Override
public SqlParameterSource getSqlParameterSource() {
final MapSqlParameterSource paramSource = new MapSqlParameterSource();
paramSource.addValues(params);
return paramSource;
}
}
<bean id="reader" class="org.wisecoding.stackoverflow.NamedParameterJdbcCursorItemReader">
<constructor-arg ref="sqlParameterSourceProvider"/>
<property name="dataSource" ref="..." />
<property name="sql" value=SELECT * FROM test WHERE col1 = :param" />
<property name="rowMapper" ref="..." />
<property name="preparedStatementSetter" ref="..." />
</bean>
<bean id="sqlParameterSourceProvider" class="org.wisecoding.stackoverflow.MyCustomSqlParameterSourceProvider">
</bean>
Currently, there is not a way to do this. The JdbcCursorItemReader uses raw JDBC (PreparedStatement) instead of the Spring JdbcTemplate under the hood (since there is no way to get the underlying ResultSet when using JdbcTemplate). If you'd like to contribute this as a new feature, or request it as a new feature, feel free to do so at jira.spring.io
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With