|
1 | 1 | # Spring Batch Support
|
2 | 2 | This library provides some utilities to make it easier to interact with the MyBatis Spring Batch support.
|
3 | 3 |
|
4 |
| -## The Problem |
| 4 | +MyBatis Spring provides support for interacting with Spring Batch (see |
| 5 | +[http://www.mybatis.org/spring/batch.html](http://www.mybatis.org/spring/batch.html)). This support consists of |
| 6 | +specialized implementations of Spring Batch's `ItemReader` and `ItemWriter` interfaces that have support for MyBatis |
| 7 | +mappers. |
5 | 8 |
|
6 |
| -MyBatis Spring support provides utility classes for interacting with Spring Batch (see [http://www.mybatis.org/spring/batch.html](http://www.mybatis.org/spring/batch.html)). These classes are specialized implementations of Spring Batch's `ItemReader` and `ItemWriter` interfaces that have support for MyBatis mappers. |
| 9 | +The `ItemWriter` implementation works with SQL generated by MyBatis Dynamic SQL with no modification needed. |
7 | 10 |
|
8 |
| -The `ItemWriter` implementations work with SQL generated by MyBatis Dynamic SQL with no modification needed. |
| 11 | +The `ItemReader` implementations need special care. Those classes assume that all query parameters will be placed in a |
| 12 | +Map (as per usual when using multiple parameters in a query). MyBatis Dynamic SQL, by default, builds a parameter |
| 13 | +object that is intended to be the only parameter for a query. The library contains utilities for overcoming this |
| 14 | +difficulty. |
9 | 15 |
|
10 |
| -The `ItemReader` implementations need special care. Those classes assume that all query parameters will be placed in a Map (as per usual when using multiple parameters in a query). MyBatis Dynamic SQL, by default, builds a parameter object that should be the only parameter in a query and will not work when placed in a Map of parameters. |
| 16 | +## Using MyBatisCursorItemReader |
11 | 17 |
|
12 |
| -## The Solution |
| 18 | +The `MyBatisCursorItemReader` class works with built-in support for cursor based queries in MyBatis. Queries of this |
| 19 | +type will read row by row and MyBatis will convert each result row to a result object without having to read the entire |
| 20 | +result set into memory. The normal rendering for MyBatis will work for queries using this reader, but special care |
| 21 | +must be taken to prepare the parameter values for use with this reader. See the following example: |
13 | 22 |
|
14 |
| -The solution involves these steps: |
15 |
| - |
16 |
| -1. The SQL must be rendered such that the parameter markers are aware of the enclosing parameter Map in the `ItemReader` |
17 |
| -1. The `SelectStatementProvider` must be placed in the `ItemReader` parameter Map with a known key. |
18 |
| -1. The `@SelectProvider` must be configured to be aware of the enclosing parameter Map |
19 |
| - |
20 |
| -MyBatis Dynamic SQL provides utilities for each of these requirements. Each utility uses a shared Map key for consistency. |
21 |
| - |
22 |
| -## Spring Batch Item Readers |
23 |
| - |
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 |
| 23 | +```java |
| 24 | +@Bean |
| 25 | +public MyBatisCursorItemReader<PersonRecord> reader(SqlSessionFactory sqlSessionFactory) { |
| 26 | + SelectStatementProvider selectStatement = select(person.allColumns()) |
| 27 | + .from(person) |
| 28 | + .where(lastName, isEqualTo("flintstone")) |
| 29 | + .build() |
| 30 | + .render(RenderingStrategies.MYBATIS3); |
| 31 | + |
| 32 | + MyBatisCursorItemReader<PersonRecord> reader = new MyBatisCursorItemReader<>(); |
| 33 | + reader.setQueryId(PersonMapper.class.getName() + ".selectMany"); |
| 34 | + reader.setSqlSessionFactory(sqlSessionFactory); |
| 35 | + reader.setParameterValues(SpringBatchUtility.toParameterValues(selectStatement)); |
| 36 | + return reader; |
| 37 | +} |
| 38 | +``` |
38 | 39 |
|
39 |
| -Queries intended for the `MyBatisCursorItemReader` should be rendered as follows: |
| 40 | +Note the use of `SpringBatchUtility.toParameterValues(...)`. This utility will set up the parameter Map correctly for the |
| 41 | +rendered statement, and for use with a library supplied `@selectProvider`. See the following for an example of the mapper |
| 42 | +method used for the query coded above: |
40 | 43 |
|
41 | 44 | ```java
|
42 |
| - SelectStatementProvider selectStatement = SpringBatchUtility.selectForCursor(person.allColumns()) |
43 |
| - .from(person) |
44 |
| - .where(lastName, isEqualTo("flintstone")) |
45 |
| - .build() |
46 |
| - .render(); // renders for MyBatisCursorItemReader |
| 45 | +@Mapper |
| 46 | +public interface PersonMapper { |
| 47 | + |
| 48 | + @SelectProvider(type=SpringBatchProviderAdapter.class, method="select") |
| 49 | + @Results({ |
| 50 | + @Result(column="id", property="id", id=true), |
| 51 | + @Result(column="first_name", property="firstName"), |
| 52 | + @Result(column="last_name", property="lastName") |
| 53 | + }) |
| 54 | + List<PersonRecord> selectMany(Map<String, Object> parameterValues); |
| 55 | +} |
47 | 56 | ```
|
48 | 57 |
|
49 |
| -### Rendering for Paging |
| 58 | +Note the use of the `SpringBatchProviderAdapter` - that adapter knows how to retrieve the rendered queries from the |
| 59 | +parameter map initialed in the method above. |
50 | 60 |
|
51 |
| -Queries intended for the `MyBatisPagingItemReader` should be rendered as follows: |
| 61 | +### Migrating from 1.x Support for MyBatisCursorItemReader |
| 62 | + |
| 63 | +In version 1.x, the library supplied a special utility for creating a select statement as follows: |
52 | 64 |
|
53 | 65 | ```java
|
54 |
| - SelectStatementProvider selectStatement = SpringBatchUtility.selectForPaging(person.allColumns()) |
55 |
| - .from(person) |
56 |
| - .where(lastName, isEqualTo("flintstone")) |
57 |
| - .build() |
58 |
| - .render(); // renders for MyBatisPagingItemReader |
| 66 | +SelectStatementProvider selectStatement = SpringBatchUtility.selectForCursor(person.allColumns()) |
| 67 | + .from(person) |
| 68 | + .where(lastName, isEqualTo("flintstone")) |
| 69 | + .build() |
| 70 | + .render(); |
59 | 71 | ```
|
60 | 72 |
|
61 |
| -## Creating the Parameter Map |
| 73 | +That utility method was limited in capability. The new method allows the full capabilities of the library. To migrate, |
| 74 | +follow these steps: |
62 | 75 |
|
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: |
| 76 | +1. Replace `SpringBatchUtility.selectForCursor(...)` with `SqlBuilder.select(...)` |
| 77 | +2. Replace `render()` with `render(RenderingStrategies.MYBATIS3)` |
64 | 78 |
|
65 |
| -For cursor based queries... |
| 79 | +## Using MyBatisPagingItemReader |
66 | 80 |
|
67 |
| -```java |
68 |
| - MyBatisCursorItemReader<Person> reader = new MyBatisCursorItemReader<>(); |
69 |
| - reader.setQueryId(PersonMapper.class.getName() + ".selectMany"); |
70 |
| - reader.setSqlSessionFactory(sqlSessionFactory); |
71 |
| - reader.setParameterValues(SpringBatchUtility.toParameterValues(selectStatement)); // create parameter map |
72 |
| -``` |
73 |
| -For paging based queries... |
| 81 | +The `MyBatisPagingItemReader` class works with paging queries - queries that read rows in pages and process page by page |
| 82 | +rather than row by row. The normal rendering for MyBatis will work NOT for queries using this reader because MyBatis |
| 83 | +Spring support supplies specially named parameters for page size, offset, etc. So the query must be rendered properly |
| 84 | +to respond to these parameter values that are supplied at runtime. As with the other reader, special care |
| 85 | +must also be taken to prepare the parameter values for use with this reader. See the following example: |
74 | 86 |
|
75 | 87 | ```java
|
76 |
| - MyBatisPagingItemReader<Person> reader = new MyBatisPagingItemReader<>(); |
77 |
| - reader.setQueryId(PersonMapper.class.getName() + ".selectMany"); |
78 |
| - reader.setSqlSessionFactory(sqlSessionFactory); |
79 |
| - reader.setPageSize(7); |
80 |
| - reader.setParameterValues(SpringBatchUtility.toParameterValues(selectStatement)); // create parameter map |
| 88 | +@Bean |
| 89 | +public MyBatisPagingItemReader<PersonRecord> reader(SqlSessionFactory sqlSessionFactory) { |
| 90 | + SelectStatementProvider selectStatement = select(person.allColumns()) |
| 91 | + .from(person) |
| 92 | + .where(forPagingTest, isEqualTo(true)) |
| 93 | + .orderBy(id) |
| 94 | + .limit(SpringBatchUtility.MYBATIS_SPRING_BATCH_PAGESIZE) |
| 95 | + .offset(SpringBatchUtility.MYBATIS_SPRING_BATCH_SKIPROWS) |
| 96 | + .build() |
| 97 | + .render(SpringBatchUtility.SPRING_BATCH_PAGING_ITEM_READER_RENDERING_STRATEGY); |
| 98 | + |
| 99 | + MyBatisPagingItemReader<PersonRecord> reader = new MyBatisPagingItemReader<>(); |
| 100 | + reader.setQueryId(PersonMapper.class.getName() + ".selectMany"); |
| 101 | + reader.setSqlSessionFactory(sqlSessionFactory); |
| 102 | + reader.setParameterValues(SpringBatchUtility.toParameterValues(selectStatement)); |
| 103 | + reader.setPageSize(7); |
| 104 | + return reader; |
| 105 | +} |
81 | 106 | ```
|
| 107 | +Notice the following important items: |
82 | 108 |
|
| 109 | +1. The `limit` and `offset` methods in the query are used to set up paging support in the query. With MyBatis Spring |
| 110 | + batch support, the integration library will supply values for those parameters at runtime. Any values you code in the |
| 111 | + select statement will be ignored - only the values supplied by the library will be used. We supply two constants |
| 112 | + to make this clearer: `MYBATIS_SPRING_BATCH_PAGESIZE` and `MYBATIS_SPRING_BATCH_SKIPROWS`. You can use these values |
| 113 | + to make the code clearer, but again the values will be ignored at runtime. |
| 114 | +2. The query must be rendered with the `SPRING_BATCH_PAGING_ITEM_READER_RENDERING_STRATEGY` rendering strategy. This |
| 115 | + rendering strategy will render the query so that it will respond properly to the runtime values supplied for page size |
| 116 | + and skip rows. |
83 | 117 |
|
84 |
| -## Specialized @SelectProvider Adapter |
| 118 | +### Migrating from 1.x Support for MyBatisPagingItemReader |
85 | 119 |
|
86 |
| -MyBatis mapper methods should be configured to use the specialized `@SelectProvider` adapter as follows: |
| 120 | +In version 1.x, the library supplied a special utility for creating a select statement as follows: |
87 | 121 |
|
88 | 122 | ```java
|
89 |
| - @SelectProvider(type=SpringBatchProviderAdapter.class, method="select") // use the Spring batch adapter |
90 |
| - @Results({ |
91 |
| - @Result(column="id", property="id", id=true), |
92 |
| - @Result(column="first_name", property="firstName"), |
93 |
| - @Result(column="last_name", property="lastName") |
94 |
| - }) |
95 |
| - List<Person> selectMany(Map<String, Object> parameterValues); |
| 123 | +SelectStatementProvider selectStatement = SpringBatchUtility.selectForPaging(person.allColumns()) |
| 124 | + .from(person) |
| 125 | + .where(forPagingTest, isEqualTo(true)) |
| 126 | + .orderBy(id) |
| 127 | + .build() |
| 128 | + .render(); |
96 | 129 | ```
|
97 | 130 |
|
98 |
| -## Complete Example |
| 131 | +That utility method was very limited in capability. It only supported limit and offset based queries - which are not |
| 132 | +supported in all databases. The new method allows the full capabilities of the library. To migrate, |
| 133 | +follow these steps: |
| 134 | + |
| 135 | +1. Replace `SpringBatchUtility.selectForPaging(...)` with `SqlBuilder.select(...)` |
| 136 | +2. Add `limit()`, `fetchFirst()`, and `offset()` method calls as appropriate for your query and database |
| 137 | +3. Replace `render()` with `render(RenderingStrategies.SPRING_BATCH_PAGING_ITEM_READER_RENDERING_STRATEGY)` |
| 138 | + |
| 139 | + |
| 140 | +## Complete Examples |
99 | 141 |
|
100 |
| -The unit tests for MyBatis Dynamic SQL include a complete example 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) |
| 142 | +The unit tests for MyBatis Dynamic SQL include a complete example of using MyBatis Spring Batch support using the |
| 143 | +MyBatis supplied reader as well as both types of MyBatis supplied writers. You can see the full example |
| 144 | +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) |
0 commit comments