Skip to content

Commit 66a5dee

Browse files
authored
Merge pull request #1355 from harawata/db2-result-set
Support drivers that close ResultSet automatically when next() returns false.
2 parents a0ffc71 + b21ad1e commit 66a5dee

File tree

6 files changed

+953
-524
lines changed

6 files changed

+953
-524
lines changed

src/main/java/org/apache/ibatis/cursor/defaults/DefaultCursor.java

Lines changed: 157 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -37,188 +37,190 @@
3737
*/
3838
public class DefaultCursor<T> implements Cursor<T> {
3939

40-
// ResultSetHandler stuff
41-
private final DefaultResultSetHandler resultSetHandler;
42-
private final ResultMap resultMap;
43-
private final ResultSetWrapper rsw;
44-
private final RowBounds rowBounds;
45-
private final ObjectWrapperResultHandler<T> objectWrapperResultHandler = new ObjectWrapperResultHandler<>();
46-
47-
private final CursorIterator cursorIterator = new CursorIterator();
48-
private boolean iteratorRetrieved;
49-
50-
private CursorStatus status = CursorStatus.CREATED;
51-
private int indexWithRowBound = -1;
52-
53-
private enum CursorStatus {
54-
55-
/**
56-
* A freshly created cursor, database ResultSet consuming has not started
57-
*/
58-
CREATED,
59-
/**
60-
* A cursor currently in use, database ResultSet consuming has started
61-
*/
62-
OPEN,
63-
/**
64-
* A closed cursor, not fully consumed
65-
*/
66-
CLOSED,
67-
/**
68-
* A fully consumed cursor, a consumed cursor is always closed
69-
*/
70-
CONSUMED
40+
// ResultSetHandler stuff
41+
private final DefaultResultSetHandler resultSetHandler;
42+
private final ResultMap resultMap;
43+
private final ResultSetWrapper rsw;
44+
private final RowBounds rowBounds;
45+
private final ObjectWrapperResultHandler<T> objectWrapperResultHandler = new ObjectWrapperResultHandler<>();
46+
47+
private final CursorIterator cursorIterator = new CursorIterator();
48+
private boolean iteratorRetrieved;
49+
50+
private CursorStatus status = CursorStatus.CREATED;
51+
private int indexWithRowBound = -1;
52+
53+
private enum CursorStatus {
54+
55+
/**
56+
* A freshly created cursor, database ResultSet consuming has not started
57+
*/
58+
CREATED,
59+
/**
60+
* A cursor currently in use, database ResultSet consuming has started
61+
*/
62+
OPEN,
63+
/**
64+
* A closed cursor, not fully consumed
65+
*/
66+
CLOSED,
67+
/**
68+
* A fully consumed cursor, a consumed cursor is always closed
69+
*/
70+
CONSUMED
71+
}
72+
73+
public DefaultCursor(DefaultResultSetHandler resultSetHandler, ResultMap resultMap, ResultSetWrapper rsw, RowBounds rowBounds) {
74+
this.resultSetHandler = resultSetHandler;
75+
this.resultMap = resultMap;
76+
this.rsw = rsw;
77+
this.rowBounds = rowBounds;
78+
}
79+
80+
@Override
81+
public boolean isOpen() {
82+
return status == CursorStatus.OPEN;
83+
}
84+
85+
@Override
86+
public boolean isConsumed() {
87+
return status == CursorStatus.CONSUMED;
88+
}
89+
90+
@Override
91+
public int getCurrentIndex() {
92+
return rowBounds.getOffset() + cursorIterator.iteratorIndex;
93+
}
94+
95+
@Override
96+
public Iterator<T> iterator() {
97+
if (iteratorRetrieved) {
98+
throw new IllegalStateException("Cannot open more than one iterator on a Cursor");
7199
}
72-
73-
public DefaultCursor(DefaultResultSetHandler resultSetHandler, ResultMap resultMap, ResultSetWrapper rsw, RowBounds rowBounds) {
74-
this.resultSetHandler = resultSetHandler;
75-
this.resultMap = resultMap;
76-
this.rsw = rsw;
77-
this.rowBounds = rowBounds;
100+
if (isClosed()) {
101+
throw new IllegalStateException("A Cursor is already closed.");
78102
}
79-
80-
@Override
81-
public boolean isOpen() {
82-
return status == CursorStatus.OPEN;
103+
iteratorRetrieved = true;
104+
return cursorIterator;
105+
}
106+
107+
@Override
108+
public void close() {
109+
if (isClosed()) {
110+
return;
83111
}
84112

85-
@Override
86-
public boolean isConsumed() {
87-
return status == CursorStatus.CONSUMED;
88-
}
113+
ResultSet rs = rsw.getResultSet();
114+
try {
115+
if (rs != null) {
116+
Statement statement = rs.getStatement();
89117

90-
@Override
91-
public int getCurrentIndex() {
92-
return rowBounds.getOffset() + cursorIterator.iteratorIndex;
118+
rs.close();
119+
if (statement != null) {
120+
statement.close();
121+
}
122+
}
123+
status = CursorStatus.CLOSED;
124+
} catch (SQLException e) {
125+
// ignore
93126
}
127+
}
94128

95-
@Override
96-
public Iterator<T> iterator() {
97-
if (iteratorRetrieved) {
98-
throw new IllegalStateException("Cannot open more than one iterator on a Cursor");
99-
}
100-
if (isClosed()) {
101-
throw new IllegalStateException("A Cursor is already closed.");
102-
}
103-
iteratorRetrieved = true;
104-
return cursorIterator;
129+
protected T fetchNextUsingRowBound() {
130+
T result = fetchNextObjectFromDatabase();
131+
while (result != null && indexWithRowBound < rowBounds.getOffset()) {
132+
result = fetchNextObjectFromDatabase();
105133
}
134+
return result;
135+
}
106136

107-
@Override
108-
public void close() {
109-
if (isClosed()) {
110-
return;
111-
}
137+
protected T fetchNextObjectFromDatabase() {
138+
if (isClosed()) {
139+
return null;
140+
}
112141

113-
ResultSet rs = rsw.getResultSet();
114-
try {
115-
if (rs != null) {
116-
Statement statement = rs.getStatement();
117-
118-
rs.close();
119-
if (statement != null) {
120-
statement.close();
121-
}
122-
}
123-
status = CursorStatus.CLOSED;
124-
} catch (SQLException e) {
125-
// ignore
126-
}
142+
try {
143+
status = CursorStatus.OPEN;
144+
if (!rsw.getResultSet().isClosed()) {
145+
resultSetHandler.handleRowValues(rsw, resultMap, objectWrapperResultHandler, RowBounds.DEFAULT, null);
146+
}
147+
} catch (SQLException e) {
148+
throw new RuntimeException(e);
127149
}
128150

129-
protected T fetchNextUsingRowBound() {
130-
T result = fetchNextObjectFromDatabase();
131-
while (result != null && indexWithRowBound < rowBounds.getOffset()) {
132-
result = fetchNextObjectFromDatabase();
133-
}
134-
return result;
151+
T next = objectWrapperResultHandler.result;
152+
if (next != null) {
153+
indexWithRowBound++;
154+
}
155+
// No more object or limit reached
156+
if (next == null || getReadItemsCount() == rowBounds.getOffset() + rowBounds.getLimit()) {
157+
close();
158+
status = CursorStatus.CONSUMED;
135159
}
160+
objectWrapperResultHandler.result = null;
136161

137-
protected T fetchNextObjectFromDatabase() {
138-
if (isClosed()) {
139-
return null;
140-
}
162+
return next;
163+
}
141164

142-
try {
143-
status = CursorStatus.OPEN;
144-
resultSetHandler.handleRowValues(rsw, resultMap, objectWrapperResultHandler, RowBounds.DEFAULT, null);
145-
} catch (SQLException e) {
146-
throw new RuntimeException(e);
147-
}
165+
private boolean isClosed() {
166+
return status == CursorStatus.CLOSED || status == CursorStatus.CONSUMED;
167+
}
148168

149-
T next = objectWrapperResultHandler.result;
150-
if (next != null) {
151-
indexWithRowBound++;
152-
}
153-
// No more object or limit reached
154-
if (next == null || getReadItemsCount() == rowBounds.getOffset() + rowBounds.getLimit()) {
155-
close();
156-
status = CursorStatus.CONSUMED;
157-
}
158-
objectWrapperResultHandler.result = null;
169+
private int getReadItemsCount() {
170+
return indexWithRowBound + 1;
171+
}
159172

160-
return next;
161-
}
173+
private static class ObjectWrapperResultHandler<T> implements ResultHandler<T> {
162174

163-
private boolean isClosed() {
164-
return status == CursorStatus.CLOSED || status == CursorStatus.CONSUMED;
165-
}
175+
private T result;
166176

167-
private int getReadItemsCount() {
168-
return indexWithRowBound + 1;
177+
@Override
178+
public void handleResult(ResultContext<? extends T> context) {
179+
this.result = context.getResultObject();
180+
context.stop();
169181
}
182+
}
170183

171-
private static class ObjectWrapperResultHandler<T> implements ResultHandler<T> {
172-
173-
private T result;
184+
private class CursorIterator implements Iterator<T> {
174185

175-
@Override
176-
public void handleResult(ResultContext<? extends T> context) {
177-
this.result = context.getResultObject();
178-
context.stop();
179-
}
180-
}
186+
/**
187+
* Holder for the next object to be returned
188+
*/
189+
T object;
181190

182-
private class CursorIterator implements Iterator<T> {
191+
/**
192+
* Index of objects returned using next(), and as such, visible to users.
193+
*/
194+
int iteratorIndex = -1;
183195

184-
/**
185-
* Holder for the next object to be returned
186-
*/
187-
T object;
196+
@Override
197+
public boolean hasNext() {
198+
if (object == null) {
199+
object = fetchNextUsingRowBound();
200+
}
201+
return object != null;
202+
}
188203

189-
/**
190-
* Index of objects returned using next(), and as such, visible to users.
191-
*/
192-
int iteratorIndex = -1;
204+
@Override
205+
public T next() {
206+
// Fill next with object fetched from hasNext()
207+
T next = object;
193208

194-
@Override
195-
public boolean hasNext() {
196-
if (object == null) {
197-
object = fetchNextUsingRowBound();
198-
}
199-
return object != null;
200-
}
209+
if (next == null) {
210+
next = fetchNextUsingRowBound();
211+
}
201212

202-
@Override
203-
public T next() {
204-
// Fill next with object fetched from hasNext()
205-
T next = object;
206-
207-
if (next == null) {
208-
next = fetchNextUsingRowBound();
209-
}
210-
211-
if (next != null) {
212-
object = null;
213-
iteratorIndex++;
214-
return next;
215-
}
216-
throw new NoSuchElementException();
217-
}
213+
if (next != null) {
214+
object = null;
215+
iteratorIndex++;
216+
return next;
217+
}
218+
throw new NoSuchElementException();
219+
}
218220

219-
@Override
220-
public void remove() {
221-
throw new UnsupportedOperationException("Cannot remove element from Cursor");
222-
}
221+
@Override
222+
public void remove() {
223+
throw new UnsupportedOperationException("Cannot remove element from Cursor");
223224
}
225+
}
224226
}

0 commit comments

Comments
 (0)