1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.ibatis.cursor.defaults;
17
18 import java.sql.ResultSet;
19 import java.sql.SQLException;
20 import java.util.Iterator;
21 import java.util.NoSuchElementException;
22
23 import org.apache.ibatis.cursor.Cursor;
24 import org.apache.ibatis.executor.resultset.DefaultResultSetHandler;
25 import org.apache.ibatis.executor.resultset.ResultSetWrapper;
26 import org.apache.ibatis.mapping.ResultMap;
27 import org.apache.ibatis.session.ResultContext;
28 import org.apache.ibatis.session.ResultHandler;
29 import org.apache.ibatis.session.RowBounds;
30
31
32
33
34
35
36
37 public class DefaultCursor<T> implements Cursor<T> {
38
39
40 private final DefaultResultSetHandler resultSetHandler;
41 private final ResultMap resultMap;
42 private final ResultSetWrapper rsw;
43 private final RowBounds rowBounds;
44 protected final ObjectWrapperResultHandler<T> objectWrapperResultHandler = new ObjectWrapperResultHandler<>();
45
46 private final CursorIterator cursorIterator = new CursorIterator();
47 private boolean iteratorRetrieved;
48
49 private CursorStatus status = CursorStatus.CREATED;
50 private int indexWithRowBound = -1;
51
52 private enum CursorStatus {
53
54
55
56
57 CREATED,
58
59
60
61 OPEN,
62
63
64
65 CLOSED,
66
67
68
69 CONSUMED
70 }
71
72 public DefaultCursor(DefaultResultSetHandler resultSetHandler, ResultMap resultMap, ResultSetWrapper rsw, RowBounds rowBounds) {
73 this.resultSetHandler = resultSetHandler;
74 this.resultMap = resultMap;
75 this.rsw = rsw;
76 this.rowBounds = rowBounds;
77 }
78
79 @Override
80 public boolean isOpen() {
81 return status == CursorStatus.OPEN;
82 }
83
84 @Override
85 public boolean isConsumed() {
86 return status == CursorStatus.CONSUMED;
87 }
88
89 @Override
90 public int getCurrentIndex() {
91 return rowBounds.getOffset() + cursorIterator.iteratorIndex;
92 }
93
94 @Override
95 public Iterator<T> iterator() {
96 if (iteratorRetrieved) {
97 throw new IllegalStateException("Cannot open more than one iterator on a Cursor");
98 }
99 if (isClosed()) {
100 throw new IllegalStateException("A Cursor is already closed.");
101 }
102 iteratorRetrieved = true;
103 return cursorIterator;
104 }
105
106 @Override
107 public void close() {
108 if (isClosed()) {
109 return;
110 }
111
112 ResultSet rs = rsw.getResultSet();
113 try {
114 if (rs != null) {
115 rs.close();
116 }
117 } catch (SQLException e) {
118
119 } finally {
120 status = CursorStatus.CLOSED;
121 }
122 }
123
124 protected T fetchNextUsingRowBound() {
125 T result = fetchNextObjectFromDatabase();
126 while (objectWrapperResultHandler.fetched && indexWithRowBound < rowBounds.getOffset()) {
127 result = fetchNextObjectFromDatabase();
128 }
129 return result;
130 }
131
132 protected T fetchNextObjectFromDatabase() {
133 if (isClosed()) {
134 return null;
135 }
136
137 try {
138 objectWrapperResultHandler.fetched = false;
139 status = CursorStatus.OPEN;
140 if (!rsw.getResultSet().isClosed()) {
141 resultSetHandler.handleRowValues(rsw, resultMap, objectWrapperResultHandler, RowBounds.DEFAULT, null);
142 }
143 } catch (SQLException e) {
144 throw new RuntimeException(e);
145 }
146
147 T next = objectWrapperResultHandler.result;
148 if (objectWrapperResultHandler.fetched) {
149 indexWithRowBound++;
150 }
151
152 if (!objectWrapperResultHandler.fetched || getReadItemsCount() == rowBounds.getOffset() + rowBounds.getLimit()) {
153 close();
154 status = CursorStatus.CONSUMED;
155 }
156 objectWrapperResultHandler.result = null;
157
158 return next;
159 }
160
161 private boolean isClosed() {
162 return status == CursorStatus.CLOSED || status == CursorStatus.CONSUMED;
163 }
164
165 private int getReadItemsCount() {
166 return indexWithRowBound + 1;
167 }
168
169 protected static class ObjectWrapperResultHandler<T> implements ResultHandler<T> {
170
171 protected T result;
172 protected boolean fetched;
173
174 @Override
175 public void handleResult(ResultContext<? extends T> context) {
176 this.result = context.getResultObject();
177 context.stop();
178 fetched = true;
179 }
180 }
181
182 protected class CursorIterator implements Iterator<T> {
183
184
185
186
187 T object;
188
189
190
191
192 int iteratorIndex = -1;
193
194 @Override
195 public boolean hasNext() {
196 if (!objectWrapperResultHandler.fetched) {
197 object = fetchNextUsingRowBound();
198 }
199 return objectWrapperResultHandler.fetched;
200 }
201
202 @Override
203 public T next() {
204
205 T next = object;
206
207 if (!objectWrapperResultHandler.fetched) {
208 next = fetchNextUsingRowBound();
209 }
210
211 if (objectWrapperResultHandler.fetched) {
212 objectWrapperResultHandler.fetched = false;
213 object = null;
214 iteratorIndex++;
215 return next;
216 }
217 throw new NoSuchElementException();
218 }
219
220 @Override
221 public void remove() {
222 throw new UnsupportedOperationException("Cannot remove element from Cursor");
223 }
224 }
225 }