1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.ibatis.executor.resultset;
17
18 import static org.junit.jupiter.api.Assertions.assertEquals;
19 import static org.mockito.Mockito.when;
20
21 import java.sql.Connection;
22 import java.sql.DatabaseMetaData;
23 import java.sql.ResultSet;
24 import java.sql.ResultSetMetaData;
25 import java.sql.SQLException;
26 import java.sql.Statement;
27 import java.sql.Types;
28 import java.util.ArrayList;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Map;
32
33 import org.apache.ibatis.builder.StaticSqlSource;
34 import org.apache.ibatis.executor.Executor;
35 import org.apache.ibatis.executor.parameter.ParameterHandler;
36 import org.apache.ibatis.mapping.BoundSql;
37 import org.apache.ibatis.mapping.MappedStatement;
38 import org.apache.ibatis.mapping.ResultMap;
39 import org.apache.ibatis.mapping.ResultMapping;
40 import org.apache.ibatis.mapping.SqlCommandType;
41 import org.apache.ibatis.session.Configuration;
42 import org.apache.ibatis.session.ResultHandler;
43 import org.apache.ibatis.session.RowBounds;
44 import org.apache.ibatis.type.TypeHandlerRegistry;
45 import org.junit.jupiter.api.Test;
46 import org.junit.jupiter.api.extension.ExtendWith;
47 import org.mockito.Mock;
48 import org.mockito.Spy;
49 import org.mockito.junit.jupiter.MockitoExtension;
50
51 @ExtendWith(MockitoExtension.class)
52 class DefaultResultSetHandlerTest2 {
53
54 @Spy
55 private ImpatientResultSet rs;
56 @Mock
57 private Statement stmt;
58 @Mock
59 protected ResultSetMetaData rsmd;
60 @Mock
61 private Connection conn;
62 @Mock
63 private DatabaseMetaData dbmd;
64
65 @SuppressWarnings("serial")
66 @Test
67 void shouldNotCallNextOnClosedResultSet_SimpleResult() throws Exception {
68 final Configuration config = new Configuration();
69 final TypeHandlerRegistry registry = config.getTypeHandlerRegistry();
70 final MappedStatement ms = new MappedStatement.Builder(config, "testSelect",
71 new StaticSqlSource(config, "some select statement"), SqlCommandType.SELECT).resultMaps(
72 new ArrayList<ResultMap>() {
73 {
74 add(new ResultMap.Builder(config, "testMap", HashMap.class, new ArrayList<ResultMapping>() {
75 {
76 add(new ResultMapping.Builder(config, "id", "id", registry.getTypeHandler(Integer.class)).build());
77 }
78 }).build());
79 }
80 }).build();
81
82 final Executor executor = null;
83 final ParameterHandler parameterHandler = null;
84 final ResultHandler<?> resultHandler = null;
85 final BoundSql boundSql = null;
86 final RowBounds rowBounds = new RowBounds(5, 1);
87 final DefaultResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, ms, parameterHandler,
88 resultHandler, boundSql, rowBounds);
89
90 when(stmt.getResultSet()).thenReturn(rs);
91 when(rsmd.getColumnCount()).thenReturn(1);
92 when(rsmd.getColumnLabel(1)).thenReturn("id");
93 when(rsmd.getColumnType(1)).thenReturn(Types.INTEGER);
94 when(rsmd.getColumnClassName(1)).thenReturn(Integer.class.getCanonicalName());
95 when(stmt.getConnection()).thenReturn(conn);
96 when(conn.getMetaData()).thenReturn(dbmd);
97 when(dbmd.supportsMultipleResultSets()).thenReturn(false);
98
99 final List<Object> results = resultSetHandler.handleResultSets(stmt);
100 assertEquals(0, results.size());
101 }
102
103 @SuppressWarnings("serial")
104 @Test
105 void shouldNotCallNextOnClosedResultSet_NestedResult() throws Exception {
106 final Configuration config = new Configuration();
107 final TypeHandlerRegistry registry = config.getTypeHandlerRegistry();
108 final ResultMap nestedResultMap = new ResultMap.Builder(config, "roleMap", HashMap.class,
109 new ArrayList<ResultMapping>() {
110 {
111 add(new ResultMapping.Builder(config, "role", "role", registry.getTypeHandler(String.class))
112 .build());
113 }
114 }).build();
115 config.addResultMap(nestedResultMap);
116 final MappedStatement ms = new MappedStatement.Builder(config, "selectPerson",
117 new StaticSqlSource(config, "select person..."),
118 SqlCommandType.SELECT).resultMaps(
119 new ArrayList<ResultMap>() {
120 {
121 add(new ResultMap.Builder(config, "personMap", HashMap.class, new ArrayList<ResultMapping>() {
122 {
123 add(new ResultMapping.Builder(config, "id", "id", registry.getTypeHandler(Integer.class))
124 .build());
125 add(new ResultMapping.Builder(config, "roles").nestedResultMapId("roleMap").build());
126 }
127 }).build());
128 }
129 })
130 .resultOrdered(true)
131 .build();
132
133 final Executor executor = null;
134 final ParameterHandler parameterHandler = null;
135 final ResultHandler<?> resultHandler = null;
136 final BoundSql boundSql = null;
137 final RowBounds rowBounds = new RowBounds(5, 1);
138 final DefaultResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, ms, parameterHandler,
139 resultHandler, boundSql, rowBounds);
140
141 when(stmt.getResultSet()).thenReturn(rs);
142 when(rsmd.getColumnCount()).thenReturn(2);
143 when(rsmd.getColumnLabel(1)).thenReturn("id");
144 when(rsmd.getColumnType(1)).thenReturn(Types.INTEGER);
145 when(rsmd.getColumnClassName(1)).thenReturn(Integer.class.getCanonicalName());
146
147 final List<Object> results = resultSetHandler.handleResultSets(stmt);
148 assertEquals(0, results.size());
149 }
150
151
152
153
154 protected abstract class ImpatientResultSet implements ResultSet {
155 private int rowIndex = -1;
156 private List<Map<String, Object>> rows = new ArrayList<>();
157
158 protected ImpatientResultSet() {
159 Map<String, Object> row = new HashMap<>();
160 row.put("id", 1);
161 row.put("role", "CEO");
162 rows.add(row);
163 }
164
165 @Override
166 public boolean next() throws SQLException {
167 throwIfClosed();
168 return ++rowIndex < rows.size();
169 }
170
171 @Override
172 public boolean isClosed() {
173 return rowIndex >= rows.size();
174 }
175
176 @Override
177 public String getString(String columnLabel) throws SQLException {
178 throwIfClosed();
179 return (String) rows.get(rowIndex).get(columnLabel);
180 }
181
182 @Override
183 public int getInt(String columnLabel) throws SQLException {
184 throwIfClosed();
185 return (Integer) rows.get(rowIndex).get(columnLabel);
186 }
187
188 @Override
189 public boolean wasNull() throws SQLException {
190 throwIfClosed();
191 return false;
192 }
193
194 @Override
195 public ResultSetMetaData getMetaData() {
196 return rsmd;
197 }
198
199 @Override
200 public int getType() throws SQLException {
201 throwIfClosed();
202 return ResultSet.TYPE_FORWARD_ONLY;
203 }
204
205 private void throwIfClosed() throws SQLException {
206 if (rowIndex >= rows.size()) {
207 throw new SQLException("Invalid operation: result set is closed.");
208 }
209 }
210 }
211 }