Spring Batch Paging с sortKeys и значениями параметров

У меня есть проект Spring Batch, работающий в Spring Boot, который работает отлично. Для моего читателя я использую JdbcPagingItemReader с MySqlPagingQueryProvider.

@Bean
public ItemReader<Person> reader(DataSource dataSource) {
    MySqlPagingQueryProvider provider = new MySqlPagingQueryProvider()
    provider.setSelectClause(ScoringConstants.SCORING_SELECT_STATEMENT)
    provider.setFromClause(ScoringConstants.SCORING_FROM_CLAUSE)
    provider.setSortKeys("p.id": Order.ASCENDING)

    JdbcPagingItemReader<Person> reader = new JdbcPagingItemReader<Person>()
    reader.setRowMapper(new PersonRowMapper())
    reader.setDataSource(dataSource)
    reader.setQueryProvider(provider)
    //Setting these caused the exception
    reader.setParameterValues(
        startDate: new Date() - 31,
        endDate: new Date()
    ) 
    reader.afterPropertiesSet()
    return reader
}

Однако, когда я изменил свой запрос с некоторыми именованными параметрами, чтобы заменить ранее жестко закодированные значения даты и установить эти значения параметров в средстве чтения, как показано выше, я получаю следующее исключение на прочитанной второй странице (первой page работает нормально, потому что параметр _id не использовался поставщиком запросов на подкачку):

org.springframework.dao.InvalidDataAccessApiUsageException: No value supplied for the SQL parameter '_id': No value registered for key '_id'
at org.springframework.jdbc.core.namedparam.NamedParameterUtils.buildValueArray(NamedParameterUtils.java:336)
at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.getPreparedStatementCreator(NamedParameterJdbcTemplate.java:374)
at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.query(NamedParameterJdbcTemplate.java:192)
at org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.query(NamedParameterJdbcTemplate.java:199)
at org.springframework.batch.item.database.JdbcPagingItemReader.doReadPage(JdbcPagingItemReader.java:218)
at org.springframework.batch.item.database.AbstractPagingItemReader.doRead(AbstractPagingItemReader.java:108)

Вот пример SQL, в котором по умолчанию нет предложения WHERE. Один создается автоматически при чтении второй страницы:

select *, (select id from family f where date_created between :startDate and :endDate and f.creator_id = p.id)  from person p

На второй странице sql изменен на следующее, однако кажется, что именованный параметр для _id не был предоставлен:

select *, (select id from family f where date_created between :startDate and :endDate and f.creator_id = p.id) from person p WHERE id > :_id

Мне интересно, могу ли я просто использовать ключи сортировки MySqlPagingQueryProvider вместе с дополнительными именованными параметрами, установленными в JdbcPagingItemReader. Если нет, то какова наилучшая альтернатива решению этой проблемы? Мне нужно иметь возможность предоставлять параметры для запроса, а также отображать его (по сравнению с использованием курсора). Благодарю вас!


person th3morg    schedule 20.11.2014    source источник
comment
Похоже, вы не устанавливаете предложение where для своих новых значений. Это просто отсутствует в вашем примере конфигурации?   -  person Michael Minella    schedule 21.11.2014
comment
@MichaelMinella Спасибо за вашу помощь. Я предоставил некоторый SQL выше. В моем первоначальном запросе нет предложения WHERE, однако провайдер подкачки автоматически добавляет его после первой страницы.   -  person th3morg    schedule 21.11.2014
comment
Я имел в виду, что когда вы настраиваете своего провайдера, вы не устанавливаете предложение where для startDate и endDate, или я что-то упустил?   -  person Michael Minella    schedule 21.11.2014
comment
@MichaelMinella, обратите внимание, что они используются не в предложении where, а скорее в подзапросе. Как вы думаете, по какой-то причине это вызовет проблему с полем _id? Я понимаю, что мог бы изменить это на соединение вместо подзапроса и использовать предложение where, но у меня есть много дополнительных подзапросов, которые нельзя превратить в соединения, которые я удалил, чтобы попытаться уменьшить путаницу. Запрос проверен и работает. Как я уже сказал, страница 1 работает нормально, и только на странице 2, где автоматически добавляется предложение WHERE, именованный параметр _id не связан. Еще раз спасибо!   -  person th3morg    schedule 21.11.2014
comment
Глядя на source и то, как работает функция getParameterMap, безусловно, покрывает это. Есть ли шанс, что вы можете включить ведение журнала и посмотреть, что выводится для использования parameterMap?   -  person David Coutu    schedule 21.11.2014
comment
th3morg, можешь собрать простой тест кейс и закинуть на гит?   -  person Michael Minella    schedule 21.11.2014
comment
Спасибо, ребята, за попытку помочь! Я думаю, что эта проблема была немного неясной и трудной для диагностики, не имея всех частей, за что я прошу прощения. Я ценю потраченное время. Спасибо еще раз!   -  person th3morg    schedule 21.11.2014


Ответы (2)


Я решил эту проблему с помощью интенсивной отладки. Оказывается, MySqlPagingQueryProvider использует метод getSortKeysWithoutAliases() при построении SQL-запроса для выполнения на первой странице и на последующих страницах. Поэтому он добавляет and (p.id > :_id) вместо and (p.id > :_p.id). Позже, когда значения сортировки второй страницы будут созданы и сохранены в поле startAfterValues JdbcPagingItemReader, будет использоваться указанная исходная строка "p.id", и в конечном итоге в карту именованных параметров будет помещена пара ("_p.id",10). Однако, когда читатель пытается заполнить _id в запросе, он не существует, потому что читатель использовал удаленный ключ без псевдонима.

Короче говоря, мне пришлось удалить ссылку на псевдоним при определении ключей сортировки.

provider.setSortKeys("p.id": Order.ASCENDING)

пришлось изменить на, чтобы все работало вместе

provider.setSortKeys("id": Order.ASCENDING)

person th3morg    schedule 21.11.2014

У меня была такая же проблема, и я получил другое возможное решение.

В моей таблице T есть поле первичного ключа INTERNAL_ID.

Запрос в JdbcPagingItemReader был таким:

SELECT INTERNAL_ID, ... FROM T WHERE ... ORDER BY INTERNAL_ID ASC

Итак, ключевой момент: в некоторых условиях запрос не возвращал результатов, а затем вызывал ошибку выше Нет значения для...

Решение:

  • Проверьте элемент Spring Batch decider на наличие строк.
  • Если это так, продолжайте с чанк: чтение-процессор-запись.
  • Если это не так, перейдите к следующему шагу.

Обратите внимание, что это два разных сценария:

  • В начале есть ряды. Вы получаете их путем подкачки, и, наконец, строк больше нет. Это не проблема, и решающий трюк не требуется.
  • В начале строк нет. Затем возникла эта ошибка, и решающий модуль решил ее.

Надеюсь это поможет.

person yaki_nuka    schedule 16.02.2016