Spring Batch:JdbcPagingItemReader无法通过setSortKeys和cast(id为unsigned)工作

时间:2017-06-05 23:17:48

标签: spring-batch

我正在使用Spring Batch

3.0.7版本Mysql

我遇到的问题是person_reader表的id列定义了varchar,因此对于简单的select 有序会发生以下情况>:

id
--
1
10 <--- !!!
2
3
...

需要什么

id
--
1
2
3
...
10 <--- ok!
... 

解决方案使用ORDER BY cast(id as unsigned)

如果我使用JdbcCursorItemReader,我可以安心使用:

setSql("SELECT id, first_name, last_name FROM person_reader ORDER BY cast(id as unsigned)");

工作正常。直到这里一切都还可以。

注意:对于批处理过程,非常重要的是以正确的顺序读取数据。

问题在于JdbcPagingItemReader

首先尝试

    JdbcPagingItemReader<Person> itemReader = new JdbcPagingItemReader<>();
    itemReader.setDataSource(dataSource);
    itemReader.setPageSize(100);
    itemReader.setRowMapper(new PersonRowMapper());

    MySqlPagingQueryProvider pagingQueryProvider = new MySqlPagingQueryProvider();
    pagingQueryProvider.setSelectClause("SELECT id, first_name, last_name");
    pagingQueryProvider.setFromClause("FROM person_reader ORDER BY cast(id as unsigned)");

    Map<String, Order> sortKeys= new HashMap<>();
    sortKeys.put("id", Order.ASCENDING);
    pagingQueryProvider.setSortKeys(sortKeys);

    itemReader.setQueryProvider(pagingQueryProvider); 

观察:

  • setFromClause("FROM person_reader ORDER BY cast(id as unsigned)")
  • put("id", Order.ASCENDING)

出现以下错误:

org.springframework.jdbc.BadSqlGrammarException: StatementCallback;
bad SQL grammar [SELECT id, first_name, last_name FROM person_reader ORDER BY cast(id as unsigned) ORDER BY id ASC LIMIT 100];
nested exception is java.sql.SQLSyntaxErrorException:
You have an error in your SQL syntax;
check the manual that corresponds to your MySQL server version for the right syntax to use near 'ORDER BY id ASC LIMIT 100' at line 1

有道理,SQL生成的SELECT id, first_name, last_name FROM person_reader ORDER BY cast(id as unsigned) ORDER BY id ASC LIMIT 100不正确。

第二次尝试

    JdbcPagingItemReader<Person> itemReader = new JdbcPagingItemReader<>();
    itemReader.setDataSource(dataSource);
    itemReader.setPageSize(100);
    itemReader.setRowMapper(new PersonRowMapper());

    MySqlPagingQueryProvider pagingQueryProvider = new MySqlPagingQueryProvider();
    pagingQueryProvider.setSelectClause("SELECT id, first_name, last_name");
    pagingQueryProvider.setFromClause("FROM person_reader");

    Map<String, Order> sortKeys= new HashMap<>();
    sortKeys.put("cast(id as unsigned)", Order.ASCENDING);
    pagingQueryProvider.setSortKeys(sortKeys);

    itemReader.setQueryProvider(pagingQueryProvider);

观察:

  • setFromClause("FROM person_reader")
  • put("cast(id as unsigned)", Order.ASCENDING)

出现以下错误:

org.springframework.jdbc.UncategorizedSQLException: StatementCallback;
uncategorized SQLException for SQL [SELECT id, first_name, last_name FROM person_reader ORDER BY cast(id as unsigned) ASC LIMIT 100];
SQL state [S0022]; error code [0]; Column 'cast(id as unsigned)' not found.;
nested exception is java.sql.SQLException: Column 'cast(id as unsigned)' not found.

对失踪&#39;感到失望。柱

Orderenum,因此无法延期。

什么是正确的方法?

α

如果我在MySQL的工作台中使用以下内容:

  • SELECT id, first_name, last_name, cast(id as unsigned) as sb_sort_column FROM person_reader ORDER BY sb_sort_column ASC

工作正常。如果我使用:

MySqlPagingQueryProvider pagingQueryProvider = new MySqlPagingQueryProvider();
pagingQueryProvider.setSelectClause("SELECT id, first_name, last_name, cast(id as unsigned) as sb_sort_column");
pagingQueryProvider.setFromClause("FROM person_reader");

Map<String, Order> sortKeys= new HashMap<>();
sortKeys.put("sb_sort_column", Order.ASCENDING);
pagingQueryProvider.setSortKeys(sortKeys);

itemReader.setQueryProvider(pagingQueryProvider);

出现以下错误:

org.springframework.jdbc.BadSqlGrammarException: 
PreparedStatementCallback; 
bad SQL grammar [SELECT id, first_name, last_name, cast(id as unsigned) as sb_sort_column FROM person_reader WHERE ((sb_sort_column > ?)) ORDER BY sb_sort_column ASC LIMIT 100]; 
nested exception is java.sql.SQLSyntaxErrorException: 
Unknown column 'sb_sort_column' in 'where clause'

从上面仔细查看关于生成的sql语句的第3行。

2 个答案:

答案 0 :(得分:1)

Manuel's earlier answer会有效,所以删除我的重复部分......

不幸的是,尽管功能上准确,但在函数上执行ORDER BY可能会运行得很差,因为它必须扫描整个表来执行cast,我不相信MySQL允许你在函数上创建一个索引。

也就是说,从MySQL 5.7.6开始,您可以向表中添加一个虚拟列CAST(ID as UNSIGNED) as SORT_ID,然后索引虚拟列。

答案 1 :(得分:0)

<强>解决方案

评论中提供的链接很有用。 来自:

因此正确的配置是:

    MySqlPagingQueryProvider pagingQueryProvider = new MySqlPagingQueryProvider();
    pagingQueryProvider.setSelectClause("SELECT id, first_name, last_name, sb_sort_column");
    pagingQueryProvider.setFromClause("FROM (SELECT id, first_name, last_name, cast(id as unsigned) as sb_sort_column FROM person_reader) person_reader");

    Map<String, Order> sortKeys= new HashMap<>();
    sortKeys.put("sb_sort_column", Order.ASCENDING);
    pagingQueryProvider.setSortKeys(sortKeys);

注意:请确保在setSelectClause方法中包含别名列,在本例中为sb_sort_column