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 @Test
66 void shouldNotCallNextOnClosedResultSet_SimpleResult() throws Exception {
67 final Configuration config = new Configuration();
68 final TypeHandlerRegistry registry = config.getTypeHandlerRegistry();
69 final MappedStatement ms = new MappedStatement.Builder(config, "testSelect",
70 new StaticSqlSource(config, "some select statement"), SqlCommandType.SELECT)
71 .resultMaps(new ArrayList<ResultMap>() {
72 private static final long serialVersionUID = 1L;
73 {
74 add(new ResultMap.Builder(config, "testMap", HashMap.class, new ArrayList<ResultMapping>() {
75 private static final long serialVersionUID = 1L;
76 {
77 add(new ResultMapping.Builder(config, "id", "id", registry.getTypeHandler(Integer.class)).build());
78 }
79 }).build());
80 }
81 }).build();
82
83 final Executor executor = null;
84 final ParameterHandler parameterHandler = null;
85 final ResultHandler<?> resultHandler = null;
86 final BoundSql boundSql = null;
87 final RowBounds rowBounds = new RowBounds(5, 1);
88 final DefaultResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, ms, parameterHandler,
89 resultHandler, boundSql, rowBounds);
90
91 when(stmt.getResultSet()).thenReturn(rs);
92 when(rsmd.getColumnCount()).thenReturn(1);
93 when(rsmd.getColumnLabel(1)).thenReturn("id");
94 when(rsmd.getColumnType(1)).thenReturn(Types.INTEGER);
95 when(rsmd.getColumnClassName(1)).thenReturn(Integer.class.getCanonicalName());
96 when(stmt.getConnection()).thenReturn(conn);
97 when(conn.getMetaData()).thenReturn(dbmd);
98 when(dbmd.supportsMultipleResultSets()).thenReturn(false);
99
100 final List<Object> results = resultSetHandler.handleResultSets(stmt);
101 assertEquals(0, results.size());
102 }
103
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 private static final long serialVersionUID = 1L;
111 {
112 add(new ResultMapping.Builder(config, "role", "role", registry.getTypeHandler(String.class)).build());
113 }
114 }).build();
115 config.addResultMap(nestedResultMap);
116 final MappedStatement ms = new MappedStatement.Builder(config, "selectPerson",
117 new StaticSqlSource(config, "select person..."), SqlCommandType.SELECT).resultMaps(new ArrayList<ResultMap>() {
118 private static final long serialVersionUID = 1L;
119 {
120 add(new ResultMap.Builder(config, "personMap", HashMap.class, new ArrayList<ResultMapping>() {
121 private static final long serialVersionUID = 1L;
122 {
123 add(new ResultMapping.Builder(config, "id", "id", registry.getTypeHandler(Integer.class)).build());
124 add(new ResultMapping.Builder(config, "roles").nestedResultMapId("roleMap").build());
125 }
126 }).build());
127 }
128 }).resultOrdered(true).build();
129
130 final Executor executor = null;
131 final ParameterHandler parameterHandler = null;
132 final ResultHandler<?> resultHandler = null;
133 final BoundSql boundSql = null;
134 final RowBounds rowBounds = new RowBounds(5, 1);
135 final DefaultResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, ms, parameterHandler,
136 resultHandler, boundSql, rowBounds);
137
138 when(stmt.getResultSet()).thenReturn(rs);
139 when(rsmd.getColumnCount()).thenReturn(2);
140 when(rsmd.getColumnLabel(1)).thenReturn("id");
141 when(rsmd.getColumnType(1)).thenReturn(Types.INTEGER);
142 when(rsmd.getColumnClassName(1)).thenReturn(Integer.class.getCanonicalName());
143
144 final List<Object> results = resultSetHandler.handleResultSets(stmt);
145 assertEquals(0, results.size());
146 }
147
148
149
150
151 protected abstract class ImpatientResultSet implements ResultSet {
152 private int rowIndex = -1;
153 private List<Map<String, Object>> rows = new ArrayList<>();
154
155 protected ImpatientResultSet() {
156 Map<String, Object> row = new HashMap<>();
157 row.put("id", 1);
158 row.put("role", "CEO");
159 rows.add(row);
160 }
161
162 @Override
163 public boolean next() throws SQLException {
164 throwIfClosed();
165 return ++rowIndex < rows.size();
166 }
167
168 @Override
169 public boolean isClosed() {
170 return rowIndex >= rows.size();
171 }
172
173 @Override
174 public String getString(String columnLabel) throws SQLException {
175 throwIfClosed();
176 return (String) rows.get(rowIndex).get(columnLabel);
177 }
178
179 @Override
180 public int getInt(String columnLabel) throws SQLException {
181 throwIfClosed();
182 return (Integer) rows.get(rowIndex).get(columnLabel);
183 }
184
185 @Override
186 public boolean wasNull() throws SQLException {
187 throwIfClosed();
188 return false;
189 }
190
191 @Override
192 public ResultSetMetaData getMetaData() {
193 return rsmd;
194 }
195
196 @Override
197 public int getType() throws SQLException {
198 throwIfClosed();
199 return ResultSet.TYPE_FORWARD_ONLY;
200 }
201
202 private void throwIfClosed() throws SQLException {
203 if (rowIndex >= rows.size()) {
204 throw new SQLException("Invalid operation: result set is closed.");
205 }
206 }
207 }
208 }