• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mybatis / mybatis-3 / 2694

11 Feb 2025 08:40PM UTC coverage: 87.217%. Remained the same
2694

Pull #3379

github

web-flow
Merge a22361d47 into 12d286ea2
Pull Request #3379: Resolve type handler based on `java.lang.reflect.Type` instead of `Class` and respect runtime JDBC type

3824 of 4643 branches covered (82.36%)

526 of 589 new or added lines in 42 files covered. (89.3%)

16 existing lines in 5 files now uncovered.

9886 of 11335 relevant lines covered (87.22%)

0.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.54
/src/main/java/org/apache/ibatis/jdbc/SqlRunner.java
1
/*
2
 *    Copyright 2009-2025 the original author or authors.
3
 *
4
 *    Licensed under the Apache License, Version 2.0 (the "License");
5
 *    you may not use this file except in compliance with the License.
6
 *    You may obtain a copy of the License at
7
 *
8
 *       https://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 *    Unless required by applicable law or agreed to in writing, software
11
 *    distributed under the License is distributed on an "AS IS" BASIS,
12
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 *    See the License for the specific language governing permissions and
14
 *    limitations under the License.
15
 */
16
package org.apache.ibatis.jdbc;
17

18
import java.sql.Connection;
19
import java.sql.PreparedStatement;
20
import java.sql.ResultSet;
21
import java.sql.ResultSetMetaData;
22
import java.sql.SQLException;
23
import java.sql.Statement;
24
import java.util.ArrayList;
25
import java.util.HashMap;
26
import java.util.Iterator;
27
import java.util.List;
28
import java.util.Locale;
29
import java.util.Map;
30

31
import org.apache.ibatis.io.Resources;
32
import org.apache.ibatis.type.ObjectTypeHandler;
33
import org.apache.ibatis.type.TypeHandler;
34
import org.apache.ibatis.type.TypeHandlerRegistry;
35

36
/**
37
 * @author Clinton Begin
38
 */
39
public class SqlRunner {
40

41
  public static final int NO_GENERATED_KEY = Integer.MIN_VALUE + 1001;
42

43
  private final Connection connection;
44
  private final TypeHandlerRegistry typeHandlerRegistry;
45
  private boolean useGeneratedKeySupport;
46

47
  public SqlRunner(Connection connection) {
1✔
48
    this.connection = connection;
1✔
49
    this.typeHandlerRegistry = new TypeHandlerRegistry();
1✔
50
  }
1✔
51

52
  public void setUseGeneratedKeySupport(boolean useGeneratedKeySupport) {
53
    this.useGeneratedKeySupport = useGeneratedKeySupport;
1✔
54
  }
1✔
55

56
  /**
57
   * Executes a SELECT statement that returns one row.
58
   *
59
   * @param sql
60
   *          The SQL
61
   * @param args
62
   *          The arguments to be set on the statement.
63
   *
64
   * @return The row expected.
65
   *
66
   * @throws SQLException
67
   *           If less or more than one row is returned
68
   */
69
  public Map<String, Object> selectOne(String sql, Object... args) throws SQLException {
70
    List<Map<String, Object>> results = selectAll(sql, args);
1✔
71
    if (results.size() != 1) {
1!
72
      throw new SQLException("Statement returned " + results.size() + " results where exactly one (1) was expected.");
×
73
    }
74
    return results.get(0);
1✔
75
  }
76

77
  /**
78
   * Executes a SELECT statement that returns multiple rows.
79
   *
80
   * @param sql
81
   *          The SQL
82
   * @param args
83
   *          The arguments to be set on the statement.
84
   *
85
   * @return The list of rows expected.
86
   *
87
   * @throws SQLException
88
   *           If statement preparation or execution fails
89
   */
90
  public List<Map<String, Object>> selectAll(String sql, Object... args) throws SQLException {
91
    try (PreparedStatement ps = connection.prepareStatement(sql)) {
1✔
92
      setParameters(ps, args);
1✔
93
      try (ResultSet rs = ps.executeQuery()) {
1✔
94
        return getResults(rs);
1✔
95
      }
96
    }
97
  }
98

99
  /**
100
   * Executes an INSERT statement.
101
   *
102
   * @param sql
103
   *          The SQL
104
   * @param args
105
   *          The arguments to be set on the statement.
106
   *
107
   * @return The number of rows impacted or BATCHED_RESULTS if the statements are being batched.
108
   *
109
   * @throws SQLException
110
   *           If statement preparation or execution fails
111
   */
112
  public int insert(String sql, Object... args) throws SQLException {
113
    PreparedStatement ps;
114
    if (useGeneratedKeySupport) {
1!
115
      ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
1✔
116
    } else {
117
      ps = connection.prepareStatement(sql);
×
118
    }
119

120
    try {
121
      setParameters(ps, args);
1✔
122
      ps.executeUpdate();
1✔
123
      if (useGeneratedKeySupport) {
1!
124
        try (ResultSet generatedKeys = ps.getGeneratedKeys()) {
1✔
125
          List<Map<String, Object>> keys = getResults(generatedKeys);
1✔
126
          if (keys.size() == 1) {
1!
127
            Map<String, Object> key = keys.get(0);
1✔
128
            Iterator<Object> i = key.values().iterator();
1✔
129
            if (i.hasNext()) {
1!
130
              Object genkey = i.next();
1✔
131
              if (genkey != null) {
1!
132
                try {
133
                  return Integer.parseInt(genkey.toString());
1✔
134
                } catch (NumberFormatException e) {
×
135
                  // ignore, no numeric key support
136
                }
137
              }
138
            }
139
          }
140
        }
1!
141
      }
142
      return NO_GENERATED_KEY;
×
143
    } finally {
144
      try {
145
        ps.close();
1✔
146
      } catch (SQLException e) {
×
147
        // ignore
148
      }
1✔
149
    }
150
  }
151

152
  /**
153
   * Executes an UPDATE statement.
154
   *
155
   * @param sql
156
   *          The SQL
157
   * @param args
158
   *          The arguments to be set on the statement.
159
   *
160
   * @return The number of rows impacted or BATCHED_RESULTS if the statements are being batched.
161
   *
162
   * @throws SQLException
163
   *           If statement preparation or execution fails
164
   */
165
  public int update(String sql, Object... args) throws SQLException {
166
    try (PreparedStatement ps = connection.prepareStatement(sql)) {
1✔
167
      setParameters(ps, args);
1✔
168
      return ps.executeUpdate();
1✔
169
    }
170
  }
171

172
  /**
173
   * Executes a DELETE statement.
174
   *
175
   * @param sql
176
   *          The SQL
177
   * @param args
178
   *          The arguments to be set on the statement.
179
   *
180
   * @return The number of rows impacted or BATCHED_RESULTS if the statements are being batched.
181
   *
182
   * @throws SQLException
183
   *           If statement preparation or execution fails
184
   */
185
  public int delete(String sql, Object... args) throws SQLException {
186
    return update(sql, args);
1✔
187
  }
188

189
  /**
190
   * Executes any string as a JDBC Statement. Good for DDL
191
   *
192
   * @param sql
193
   *          The SQL
194
   *
195
   * @throws SQLException
196
   *           If statement preparation or execution fails
197
   */
198
  public void run(String sql) throws SQLException {
199
    try (Statement stmt = connection.createStatement()) {
1✔
200
      stmt.execute(sql);
1✔
201
    }
202
  }
1✔
203

204
  /**
205
   * @deprecated Since 3.5.4, this method is deprecated. Please close the {@link Connection} outside of this class.
206
   */
207
  @Deprecated
208
  public void closeConnection() {
209
    try {
210
      connection.close();
×
211
    } catch (SQLException e) {
×
212
      // ignore
213
    }
×
214
  }
×
215

216
  private void setParameters(PreparedStatement ps, Object... args) throws SQLException {
217
    for (int i = 0, n = args.length; i < n; i++) {
1✔
218
      if (args[i] == null) {
1!
219
        throw new SQLException(
×
220
            "SqlRunner requires an instance of Null to represent typed null values for JDBC compatibility");
221
      }
222
      if (args[i] instanceof Null) {
1✔
223
        ((Null) args[i]).getTypeHandler().setParameter(ps, i + 1, null, ((Null) args[i]).getJdbcType());
1✔
224
      } else {
225
        TypeHandler typeHandler = typeHandlerRegistry.getTypeHandler(args[i].getClass());
1✔
226
        if (typeHandler == null) {
1!
227
          throw new SQLException("SqlRunner could not find a TypeHandler instance for " + args[i].getClass());
×
228
        } else {
229
          typeHandler.setParameter(ps, i + 1, args[i], null);
1✔
230
        }
231
      }
232
    }
233
  }
1✔
234

235
  private List<Map<String, Object>> getResults(ResultSet rs) throws SQLException {
236
    List<Map<String, Object>> list = new ArrayList<>();
1✔
237
    List<String> columns = new ArrayList<>();
1✔
238
    List<TypeHandler<?>> typeHandlers = new ArrayList<>();
1✔
239
    ResultSetMetaData rsmd = rs.getMetaData();
1✔
240
    for (int i = 0, n = rsmd.getColumnCount(); i < n; i++) {
1✔
241
      columns.add(rsmd.getColumnLabel(i + 1));
1✔
242
      try {
243
        Class<?> type = Resources.classForName(rsmd.getColumnClassName(i + 1));
1✔
244
        TypeHandler<?> typeHandler = typeHandlerRegistry.getTypeHandler(type);
1✔
245
        if (typeHandler == null) {
1✔
246
          typeHandler = ObjectTypeHandler.INSTANCE;
1✔
247
        }
248
        typeHandlers.add(typeHandler);
1✔
249
      } catch (Exception e) {
×
NEW
250
        typeHandlers.add(ObjectTypeHandler.INSTANCE);
×
251
      }
1✔
252
    }
253
    while (rs.next()) {
1✔
254
      Map<String, Object> row = new HashMap<>();
1✔
255
      for (int i = 0, n = columns.size(); i < n; i++) {
1✔
256
        String name = columns.get(i);
1✔
257
        TypeHandler<?> handler = typeHandlers.get(i);
1✔
258
        row.put(name.toUpperCase(Locale.ENGLISH), handler.getResult(rs, name));
1✔
259
      }
260
      list.add(row);
1✔
261
    }
1✔
262
    return list;
1✔
263
  }
264

265
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc