Skip to content

Commit 244df51

Browse files
committed
Add support for paging queries
1 parent 1c37e4d commit 244df51

File tree

14 files changed

+503
-23
lines changed

14 files changed

+503
-23
lines changed

src/main/java/org/mybatis/dynamic/sql/util/ValueMapping.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ public <R> R accept(UpdateMappingVisitor<R> visitor) {
3838
}
3939

4040
public static <T> ValueMapping<T> of(SqlColumn<T> column, Supplier<T> valueSupplier) {
41-
ValueMapping<T> mapping = new ValueMapping<>(column, valueSupplier);
42-
return mapping;
41+
return new ValueMapping<>(column, valueSupplier);
4342
}
4443
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* Copyright 2016-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.mybatis.dynamic.sql.util.springbatch;
17+
18+
import org.mybatis.dynamic.sql.select.SelectModel;
19+
import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
20+
21+
public class SpringBatchCursorReaderSelectModel {
22+
23+
private SelectModel selectModel;
24+
25+
public SpringBatchCursorReaderSelectModel(SelectModel selectModel) {
26+
this.selectModel = selectModel;
27+
}
28+
29+
public SelectStatementProvider render() {
30+
return selectModel.render(SpringBatchUtility.SPRING_BATCH_READER_RENDERING_STRATEGY);
31+
}
32+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* Copyright 2016-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.mybatis.dynamic.sql.util.springbatch;
17+
18+
import java.util.HashMap;
19+
import java.util.Map;
20+
21+
import org.mybatis.dynamic.sql.select.SelectModel;
22+
import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
23+
24+
public class SpringBatchPagingReaderSelectModel {
25+
26+
private SelectModel selectModel;
27+
28+
public SpringBatchPagingReaderSelectModel(SelectModel selectModel) {
29+
this.selectModel = selectModel;
30+
}
31+
32+
public SelectStatementProvider render() {
33+
SelectStatementProvider selectStatement =
34+
selectModel.render(SpringBatchUtility.SPRING_BATCH_READER_RENDERING_STRATEGY);
35+
return new LimitAndOffsetDecorator(selectStatement);
36+
}
37+
38+
public class LimitAndOffsetDecorator implements SelectStatementProvider {
39+
private Map<String, Object> parameters = new HashMap<>();
40+
private String selectStatement;
41+
42+
public LimitAndOffsetDecorator(SelectStatementProvider delegate) {
43+
parameters.putAll(delegate.getParameters());
44+
45+
selectStatement = delegate.getSelectStatement()
46+
+ " LIMIT #{_pagesize} OFFSET #{_skiprows}"; //$NON-NLS-1$
47+
}
48+
49+
@Override
50+
public Map<String, Object> getParameters() {
51+
return parameters;
52+
}
53+
54+
@Override
55+
public String getSelectStatement() {
56+
return selectStatement;
57+
}
58+
}
59+
}

src/main/java/org/mybatis/dynamic/sql/util/springbatch/SpringBatchUtility.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@
1818
import java.util.HashMap;
1919
import java.util.Map;
2020

21+
import org.mybatis.dynamic.sql.BasicColumn;
2122
import org.mybatis.dynamic.sql.render.RenderingStrategy;
23+
import org.mybatis.dynamic.sql.select.QueryExpressionDSL;
24+
import org.mybatis.dynamic.sql.select.SelectDSL;
2225
import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
2326

2427
public class SpringBatchUtility {
@@ -34,4 +37,29 @@ public static Map<String, Object> toParameterValues(SelectStatementProvider sele
3437
parameterValues.put(PARAMETER_KEY, selectStatement);
3538
return parameterValues;
3639
}
40+
41+
/**
42+
* Select builder that renders in a manner appropriate for the MyBatisPagingItemReader.
43+
*
44+
* <b>Important</b> rendered SQL will contain LIMIT and OFFSET clauses in the SELECT statement. If your database
45+
* (Oracle) does not support LIMIT and OFFSET, the queries will fail.
46+
*
47+
* @param selectList a column list for the SELECT statement
48+
* @return FromGatherer used to continue a SELECT statement
49+
*/
50+
public static QueryExpressionDSL.FromGatherer<SpringBatchPagingReaderSelectModel> selectForPaging(
51+
BasicColumn...selectList) {
52+
return SelectDSL.select(SpringBatchPagingReaderSelectModel::new, selectList);
53+
}
54+
55+
/**
56+
* Select builder that renders in a manner appropriate for the MyBatisCursorItemReader.
57+
*
58+
* @param selectList a column list for the SELECT statement
59+
* @return FromGatherer used to continue a SELECT statement
60+
*/
61+
public static QueryExpressionDSL.FromGatherer<SpringBatchCursorReaderSelectModel> selectForCursor(
62+
BasicColumn...selectList) {
63+
return SelectDSL.select(SpringBatchCursorReaderSelectModel::new, selectList);
64+
}
3765
}

src/site/markdown/docs/springBatch.md

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,30 +19,69 @@ The solution involves these steps:
1919

2020
MyBatis Dynamic SQL provides utilities for each of these requirements. Each utility uses a shared Map key for consistency.
2121

22-
### Specialized Rendering
22+
## Spring Batch Item Readers
2323

24-
MyBatis Dynamic SQL provides a specialized rendering strategy for queries used with the MyBatis Spring `ItemReader` implementations. Queries should be rendered as follows:
24+
MyBatis Spring support supplies two implementations of the `ItemReader` interface:
25+
26+
1. `org.mybatis.spring.batch.MyBatisCursorItemReader` - for queries that can be efficiently processed through a single select statement and a cursor
27+
1. `org.mybatis.spring.batch.MyBatisPagingItemReader` - for queries that should be processed as a series of paged selects. Note that MyBatis does not provide any native support for paged queries - it is up to the user to write SQL for paging. The `MyBatisPagingItemWriter` simply makes properties available that specify which page should be read currently.
28+
29+
MyBatis Dynamic SQL supplies specialized select statements that will render properly for the different implementations of `ItemReader`:
30+
31+
1. `SpringBatchUtility.selectForCursor(...)` will create a select statement that is appropriate for the `MyBatisCursorItemReader` - a single select statement that will be read with a cursor
32+
1. `SpringBatchUtility.selectForPaging(...)` will create a select statement that is appropriate for the `MyBatisPagingItemReader` - a select statement that will be called multiple times - one for each page as configured on the batch job.
33+
34+
**Very Important:** The paging implementation will only work for databases that support limit and offset in select statements. Fortunately, most databases do support this - with the notable exception of Oracle.
35+
36+
37+
### Rendering for Cursor
38+
39+
Queries intended for the `MyBatisCursorItemReader` should be rendered as follows:
40+
41+
```java
42+
SelectStatementProvider selectStatement = SpringBatchUtility.selectForCursor(person.allColumns())
43+
.from(person)
44+
.where(lastName, isEqualTo("flintstone"))
45+
.build()
46+
.render(); // renders for MyBatisCursorItemReader
47+
```
48+
49+
### Rendering for Paging
50+
51+
Queries intended for the `MyBatisPagingItemReader` should be rendered as follows:
2552

2653
```java
27-
SelectStatementProvider selectStatement = SelectDSL.select(person.allColumns())
54+
SelectStatementProvider selectStatement = SpringBatchUtility.selectForPaging(person.allColumns())
2855
.from(person)
2956
.where(lastName, isEqualTo("flintstone"))
3057
.build()
31-
.render(SpringBatchUtility.SPRING_BATCH_READER_RENDERING_STRATEGY); // render for Spring Batch
58+
.render(); // renders for MyBatisPagingItemReader
3259
```
3360

34-
### Creating the Parameter Map
61+
## Creating the Parameter Map
62+
63+
The `SpringBatchUtility` provides a method to create the parameter values Map needed by the MyBatis Spring `ItemReader` implementations. It can be used as follows:
3564

36-
The `SpringBatchUtility` provides a method to create the parameter values Map needed by the MyBatis Spring `ItemReader`. It can be used as follows:
65+
For cursor based queries...
3766

3867
```java
3968
MyBatisCursorItemReader<Person> reader = new MyBatisCursorItemReader<>();
4069
reader.setQueryId(PersonMapper.class.getName() + ".selectMany");
4170
reader.setSqlSessionFactory(sqlSessionFactory);
4271
reader.setParameterValues(SpringBatchUtility.toParameterValues(selectStatement)); // create parameter map
4372
```
73+
For paging based queries...
74+
75+
```java
76+
MyBatisPagingItemReader<Person> reader = new MyBatisPagingItemReader<>();
77+
reader.setQueryId(PersonMapper.class.getName() + ".selectMany");
78+
reader.setSqlSessionFactory(sqlSessionFactory);
79+
reader.setParameterValues(SpringBatchUtility.toParameterValues(selectStatement));
80+
reader.setPageSize(7);
81+
```
82+
4483

45-
### Specialized @SelectProvider Adapter
84+
## Specialized @SelectProvider Adapter
4685

4786
MyBatis mapper methods should be configured to use the specialized `@SelectProvider` adapter as follows:
4887

@@ -58,4 +97,4 @@ MyBatis mapper methods should be configured to use the specialized `@SelectProvi
5897

5998
## Complete Example
6099

61-
The unit tests for MyBatis Dynamic SQL include a complete example of using MyBatis Spring Batch support using both a reader and writer. You can see the full example here: [https://github.com/mybatis/mybatis-dynamic-sql/tree/master/src/test/java/examples/springbatch](https://github.com/mybatis/mybatis-dynamic-sql/tree/master/src/test/java/examples/springbatch)
100+
The unit tests for MyBatis Dynamic SQL include a complete examples of using MyBatis Spring Batch support using the MyBatis supplied reader as well as both types of MyBatis supplied writers. You can see the full example here: [https://github.com/mybatis/mybatis-dynamic-sql/tree/master/src/test/java/examples/springbatch](https://github.com/mybatis/mybatis-dynamic-sql/tree/master/src/test/java/examples/springbatch)

src/test/java/examples/springbatch/BatchConfiguration.java renamed to src/test/java/examples/springbatch/CursorReaderBatchConfiguration.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import javax.sql.DataSource;
2222

2323
import org.apache.ibatis.session.SqlSessionFactory;
24-
import org.mybatis.dynamic.sql.select.SelectDSL;
2524
import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
2625
import org.mybatis.dynamic.sql.update.render.UpdateStatementProvider;
2726
import org.mybatis.dynamic.sql.util.springbatch.SpringBatchUtility;
@@ -49,9 +48,9 @@
4948

5049
@EnableBatchProcessing
5150
@Configuration
52-
@MapperScan("examples.springbatch")
5351
@ComponentScan("examples.springbatch")
54-
public class BatchConfiguration {
52+
@MapperScan("examples.springbatch")
53+
public class CursorReaderBatchConfiguration {
5554

5655
@Autowired
5756
private JobBuilderFactory jobBuilderFactory;
@@ -82,11 +81,11 @@ public JobLauncherTestUtils jobLauncherTestUtils() {
8281

8382
@Bean
8483
public MyBatisCursorItemReader<Person> reader(SqlSessionFactory sqlSessionFactory) {
85-
SelectStatementProvider selectStatement = SelectDSL.select(person.allColumns())
84+
SelectStatementProvider selectStatement = SpringBatchUtility.selectForCursor(person.allColumns())
8685
.from(person)
8786
.where(lastName, isEqualTo("flintstone"))
8887
.build()
89-
.render(SpringBatchUtility.SPRING_BATCH_READER_RENDERING_STRATEGY);
88+
.render();
9089

9190
MyBatisCursorItemReader<Person> reader = new MyBatisCursorItemReader<>();
9291
reader.setQueryId(PersonMapper.class.getName() + ".selectMany");
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/**
2+
* Copyright 2016-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package examples.springbatch;
17+
18+
import static examples.springbatch.PersonDynamicSqlSupport.*;
19+
import static org.mybatis.dynamic.sql.SqlBuilder.*;
20+
21+
import javax.sql.DataSource;
22+
23+
import org.apache.ibatis.session.SqlSessionFactory;
24+
import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
25+
import org.mybatis.dynamic.sql.update.render.UpdateStatementProvider;
26+
import org.mybatis.dynamic.sql.util.springbatch.SpringBatchUtility;
27+
import org.mybatis.spring.SqlSessionFactoryBean;
28+
import org.mybatis.spring.annotation.MapperScan;
29+
import org.mybatis.spring.batch.MyBatisBatchItemWriter;
30+
import org.mybatis.spring.batch.MyBatisPagingItemReader;
31+
import org.springframework.batch.core.Job;
32+
import org.springframework.batch.core.Step;
33+
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
34+
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
35+
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
36+
import org.springframework.batch.core.launch.support.RunIdIncrementer;
37+
import org.springframework.batch.item.ItemProcessor;
38+
import org.springframework.batch.item.ItemReader;
39+
import org.springframework.batch.item.ItemWriter;
40+
import org.springframework.batch.test.JobLauncherTestUtils;
41+
import org.springframework.beans.factory.annotation.Autowired;
42+
import org.springframework.context.annotation.Bean;
43+
import org.springframework.context.annotation.ComponentScan;
44+
import org.springframework.context.annotation.Configuration;
45+
import org.springframework.core.convert.converter.Converter;
46+
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
47+
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
48+
49+
@EnableBatchProcessing
50+
@Configuration
51+
@ComponentScan("examples.springbatch")
52+
@MapperScan("examples.springbatch")
53+
public class PagingReaderBatchConfiguration {
54+
55+
@Autowired
56+
private JobBuilderFactory jobBuilderFactory;
57+
58+
@Autowired
59+
private StepBuilderFactory stepBuilderFactory;
60+
61+
@Bean
62+
public DataSource dataSource() {
63+
return new EmbeddedDatabaseBuilder()
64+
.setType(EmbeddedDatabaseType.HSQL)
65+
.addScript("classpath:/examples/springbatch/schema.sql")
66+
.addScript("classpath:/examples/springbatch/data.sql")
67+
.build();
68+
}
69+
70+
@Bean
71+
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
72+
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
73+
bean.setDataSource(dataSource);
74+
return bean;
75+
}
76+
77+
@Bean
78+
public JobLauncherTestUtils jobLauncherTestUtils() {
79+
return new JobLauncherTestUtils();
80+
}
81+
82+
@Bean
83+
public MyBatisPagingItemReader<Person> reader(SqlSessionFactory sqlSessionFactory) {
84+
SelectStatementProvider selectStatement = SpringBatchUtility.selectForPaging(person.allColumns())
85+
.from(person)
86+
.where(forPagingTest, isEqualTo(true))
87+
.orderBy(id)
88+
.build()
89+
.render();
90+
91+
MyBatisPagingItemReader<Person> reader = new MyBatisPagingItemReader<>();
92+
reader.setQueryId(PersonMapper.class.getName() + ".selectMany");
93+
reader.setSqlSessionFactory(sqlSessionFactory);
94+
reader.setParameterValues(SpringBatchUtility.toParameterValues(selectStatement));
95+
reader.setPageSize(7);
96+
return reader;
97+
}
98+
99+
@Bean
100+
public MyBatisBatchItemWriter<Person> writer(SqlSessionFactory sqlSessionFactory,
101+
Converter<Person, UpdateStatementProvider> convertor) {
102+
MyBatisBatchItemWriter<Person> writer = new MyBatisBatchItemWriter<>();
103+
writer.setSqlSessionFactory(sqlSessionFactory);
104+
writer.setItemToParameterConverter(convertor);
105+
writer.setStatementId(PersonMapper.class.getName() + ".update");
106+
return writer;
107+
}
108+
109+
@Bean
110+
public Step step1(ItemReader<Person> reader, ItemProcessor<Person, Person> processor, ItemWriter<Person> writer) {
111+
return stepBuilderFactory.get("step1")
112+
.<Person, Person>chunk(7)
113+
.reader(reader)
114+
.processor(processor)
115+
.writer(writer)
116+
.build();
117+
}
118+
119+
@Bean
120+
public Job upperCaseLastName(Step step1) {
121+
return jobBuilderFactory.get("upperCaseLastName")
122+
.incrementer(new RunIdIncrementer())
123+
.flow(step1)
124+
.end()
125+
.build();
126+
}
127+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy