将Spring Batch JdbcCursorItemReader与NamedParameters一起使用

时间:2014-04-22 14:24:43

标签: spring-batch

Spring Batch JdbcCursorItemReader 可以接受 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>

如果sql使用作为占位符,这很有效,如上例所示。但是,我们预先存在的sql使用命名参数,例如SELECT * FROM test WHERE col1 = :param

有没有办法让JdbcCursorItemReader使用 NamedPreparedStatementSetter 而不是简单的PreparedStatementSetter?

由于

4 个答案:

答案 0 :(得分:3)

您可以尝试使用jobParameters。在这种情况下,您不需要任何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>
运行作业时

传递值

JobParameters param = new JobParametersBuilder().addString("col1", "value1").toJobParameters();

JobExecution execution = jobLauncher.run(job, param);

答案 1 :(得分:2)

一旦我们从春天开始没有官方解决方案,我们可以使用一种简单的方法来解决这个问题:

  1. 定义一个接口以提供SqlParameters:
  2. import org.springframework.jdbc.core.namedparam.SqlParameterSource;
    
    public interface SqlParameterSourceProvider { 
        SqlParameterSource getSqlParameterSource();
    }
    
    1. 扩展 JdbcCursorItemReader 并添加namedParameter功能。
    2. 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));
              }
          }
      }
      
      1. 创建实现接口 SqlParameterSourceProvider 的具体类,并具有状态,其中包含要在查询中使用的参数的更新值。
      2. 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;
            }
        }
        
        1. 最后,更新弹簧配置。
        2. <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>
          

答案 2 :(得分:1)

目前,没有办法做到这一点。 JdbcCursorItemReader使用原始JDBC(PreparedStatement)而不是Spring JdbcTemplate(因为在使用JdbcTemplate时无法获取基础ResultSet)。如果您想将此作为新功能投放,或将其作为新功能提出请求,请随时jira.spring.io

执行此操作

答案 3 :(得分:0)

https://jira.spring.io/browse/BATCH-2521中的原始解决方案,但不支持id in (:ids)子句。

这是增强功能。

import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import lombok.val;

import org.springframework.batch.item.database.JdbcCursorItemReader;
import org.springframework.jdbc.core.PreparedStatementCreatorFactory;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterUtils;

import java.util.Map;

@Slf4j
public class NamedParameterJdbcCursorItemReader<T> extends JdbcCursorItemReader<T> {

    protected void setNamedParametersSql(String sql, Map<String, Object> parameters) {
        val parsedSql = NamedParameterUtils.parseSqlStatement(sql);
        val paramSource = new MapSqlParameterSource(parameters);

        val sqlToUse = NamedParameterUtils.substituteNamedParameters(parsedSql, paramSource);
        val declaredParams = NamedParameterUtils.buildSqlParameterList(parsedSql, paramSource);
        val params = NamedParameterUtils.buildValueArray(parsedSql, paramSource, null);
        val pscf = new PreparedStatementCreatorFactory(sql, declaredParams);
        val pss = pscf.newPreparedStatementSetter(params);

        log.info("sql: {}", sqlToUse);
        log.info("parameters: {}", parameters);

        setSql(sqlToUse);
        setPreparedStatementSetter(pss);
    }
}

用法:

@Slf4j
public class UserItemJdbcReader extends NamedParameterJdbcCursorItemReader<UserEntity> {

    @PostConstruct
    public void init() {
        val sql = "SELECT * FROM users WHERE id IN (:ids)";

        val parameters = new HashMap<String, Object>(4);
        parameters.put("ids", Arrays.asList(1,2,3));

        setDataSource(dataSource);
        setRowMapper(new UserRowMapper());
        setNamedParametersSql(sql, parameters);
    }
}