การใช้ Spring Batch JdbcCursorItemReader กับ NamedParameters

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 ทดสอบโดยที่ col1 = :param

มีวิธีใดที่จะทำให้ JdbcCursorItemReader ทำงานร่วมกับ NamedPreparedStatementSetter แทนที่จะเป็น PreparationStatementSetter แบบธรรมดาได้หรือไม่

ขอบคุณ


person user1052610    schedule 22.04.2014    source แหล่งที่มา
comment
คุณช่วยกรุณาโพสต์รหัสเพื่อแสดงว่าคุณทำสิ่งนี้ได้อย่างไรโดยไม่มี jobParameters   -  person Jeff Cook    schedule 08.07.2018


คำตอบ (4)


คุณสามารถลองด้วย 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);
person Braj    schedule 24.04.2014
comment
มันไม่ทำงานในกรณีของฉัน :( stackoverflow.com /คำถาม/42008631/ - person John Joe; 02.02.2017

เมื่อเราไม่มีวิธีแก้ปัญหาอย่างเป็นทางการตั้งแต่ฤดูใบไม้ผลิ เราก็สามารถแก้ไขปัญหานี้ได้โดยใช้วิธีการง่ายๆ:

  1. กำหนดอินเทอร์เฟซเดียวเพื่อจัดเตรียม SqlParameters:
import org.springframework.jdbc.core.namedparam.SqlParameterSource;

public interface SqlParameterSourceProvider { 
    SqlParameterSource getSqlParameterSource();
}
  1. การขยาย JdbcCursorItemReader และเพิ่มคุณลักษณะ NameParameter
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 และมีสถานะที่มีค่าอัปเดตของพารามิเตอร์ที่จะใช้ในการสืบค้นของคุณ
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. สุดท้าย ให้อัพเดตการกำหนดค่าสปริง
<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>
person Wellington Souza    schedule 28.07.2016
comment
ตามที่ Michael Minella แนะนำ ฉันได้สร้างตั๋ว บน Spring Batch Jira และยังสร้าง สาขาของ Spring Batch บน GitHub ที่เพิ่มฟีเจอร์นี้ - person Wellington Souza; 05.08.2016
comment
ตามที่อธิบายไว้ใน jira.spring.io/browse/BATCH-2521 มี วิธีที่ง่ายกว่าในการบรรลุคุณลักษณะที่ต้องการโดยไม่ต้องแนะนำอินเทอร์เฟซใหม่ (SqlParameterSourceProvider) ขยาย JdbcCursorItemReader และใช้งาน SqlParameterSourceProvider ที่กำหนดเอง หวังว่ามันจะช่วยได้ - person Mahmoud Ben Hassine; 08.11.2018
comment
@MahmoudBenHassine มันใช้งานได้เมื่อเรามี param เป็นค่าเดียว แต่เมื่อฉันส่งรายการเป็นหนึ่งในค่าต่างๆ ฉันขาดบางสิ่งบางอย่างไปเกี่ยวกับวิธีที่ฉันสามารถใช้ประโยชน์จากโซลูชันที่วางไว้ในลิงก์ด้านบนของคุณ - person Neeraj Singh; 20.02.2019
comment
@NeerajSingh ฉันเชื่อว่าคุณกำลังอ้างถึงคำถามนี้: stackoverflow.com/questions/54782690 ดังที่ฉันได้กล่าวไว้ในคำตอบสำหรับคำถามนั้น วิธีแก้ปัญหาเดียวกันนี้ทำงานได้โดยการปรับพารามิเตอร์ให้แบนราบหรือใช้ PreparedStatementCreatorFactory ดังที่แสดงที่นี่: stackoverflow.com/ ก/57471284/5019386. ฉันเพิ่มตัวอย่างสำหรับทั้งสองกรณีใน repo นี้ - person Mahmoud Ben Hassine; 14.02.2020

ขณะนี้ยังไม่มีวิธีการทำเช่นนี้ JdbcCursorItemReader ใช้ JDBC แบบดิบ (PreparedStatement) แทน Spring JdbcTemplate ภายใต้ประทุน (เนื่องจากไม่มีวิธีรับ ResultSet พื้นฐานเมื่อใช้ JdbcTemplate) หากคุณต้องการสนับสนุนสิ่งนี้ในฐานะคุณสมบัติใหม่ หรือขอเป็นคุณสมบัติใหม่ อย่าลังเลที่จะดำเนินการได้ที่ jira สปริง.io

person Michael Minella    schedule 22.04.2014
comment
@Michael Minella - ฟีเจอร์นี้ได้รับการพัฒนาใน Spring Batch เวอร์ชัน 4.0.x หรือไม่ คุณช่วยกรุณาแสดงข้อมูลโค้ดได้ไหม - person Jeff Cook; 08.07.2018

โซลูชันดั้งเดิมใน https://jira.spring.io/browse/BATCH-2521 แต่ซึ่งไม่รองรับ id in (:ids) clause

นี่คือการปรับปรุง

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);
    }
}
person Sub    schedule 13.08.2019
comment
ฉันต้องการทราบว่าเราสามารถใช้สิ่งนี้เพื่อส่งคืนข้อมูลเป็น ItemReader ได้อย่างไร - person Vamsi; 09.06.2020