Skip to content

Commit a22361d

Browse files
committed
Making the behavior closer to the previous version
1 parent a38d06c commit a22361d

File tree

7 files changed

+84
-64
lines changed

7 files changed

+84
-64
lines changed

src/main/java/org/apache/ibatis/executor/keygen/Jdbc3KeyGenerator.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,8 +260,11 @@ protected void assign(ResultSet rs, Object param) {
260260
+ metaParam.getOriginalObject().getClass().getName() + "'.");
261261
}
262262
Class<?> propertyType = metaParam.getSetterType(propertyName);
263-
typeHandler = typeHandlerRegistry.getTypeHandler(propertyType,
264-
JdbcType.forCode(rsmd.getColumnType(columnPosition)));
263+
JdbcType jdbcType = JdbcType.forCode(rsmd.getColumnType(columnPosition));
264+
typeHandler = typeHandlerRegistry.getTypeHandler(propertyType, jdbcType);
265+
if (typeHandler == null) {
266+
typeHandler = typeHandlerRegistry.getTypeHandler(jdbcType);
267+
}
265268
}
266269
if (typeHandler == null) {
267270
// Error?

src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java

Lines changed: 15 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import java.lang.reflect.Type;
2121
import java.sql.CallableStatement;
2222
import java.sql.ResultSet;
23-
import java.sql.ResultSetMetaData;
2423
import java.sql.SQLException;
2524
import java.sql.Statement;
2625
import java.text.MessageFormat;
@@ -68,7 +67,7 @@
6867
import org.apache.ibatis.session.ResultHandler;
6968
import org.apache.ibatis.session.RowBounds;
7069
import org.apache.ibatis.type.JdbcType;
71-
import org.apache.ibatis.type.TypeException;
70+
import org.apache.ibatis.type.ObjectTypeHandler;
7271
import org.apache.ibatis.type.TypeHandler;
7372
import org.apache.ibatis.type.TypeHandlerRegistry;
7473

@@ -110,8 +109,6 @@ public class DefaultResultSetHandler implements ResultSetHandler {
110109
private final Map<String, List<UnMappedColumnAutoMapping>> autoMappingsCache = new HashMap<>();
111110
private final Map<String, List<String>> constructorAutoMappingColumns = new HashMap<>();
112111

113-
private final Map<CacheKey, TypeHandler<?>> typeHandlerCache = new HashMap<>();
114-
115112
// temporary marking flag that indicate using constructor mapping (use field to reduce memory usage)
116113
private boolean useConstructorMappings;
117114

@@ -157,7 +154,6 @@ public void handleOutputParameters(CallableStatement cs) throws SQLException {
157154
final Object parameterObject = parameterHandler.getParameterObject();
158155
final MetaObject metaParam = configuration.newMetaObject(parameterObject);
159156
final List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
160-
ResultSetMetaData rsmd = null;
161157
for (int i = 0; i < parameterMappings.size(); i++) {
162158
final ParameterMapping parameterMapping = parameterMappings.get(i);
163159
if (parameterMapping.getMode() == ParameterMode.OUT || parameterMapping.getMode() == ParameterMode.INOUT) {
@@ -167,18 +163,18 @@ public void handleOutputParameters(CallableStatement cs) throws SQLException {
167163
final String property = parameterMapping.getProperty();
168164
TypeHandler<?> typeHandler = parameterMapping.getTypeHandler();
169165
if (typeHandler == null) {
170-
Class<?> javaType = parameterMapping.getJavaType();
166+
Type javaType = parameterMapping.getJavaType();
171167
if (javaType == null || javaType == Object.class) {
172-
metaParam.getGenericSetterType(property);
168+
javaType = metaParam.getGenericSetterType(property).getKey();
173169
}
174170
JdbcType jdbcType = parameterMapping.getJdbcType();
175-
if (jdbcType == null) {
176-
if (rsmd == null) {
177-
rsmd = cs.getMetaData();
171+
typeHandler = typeHandlerRegistry.resolve(parameterObject.getClass(), javaType, jdbcType, null);
172+
if (typeHandler == null) {
173+
typeHandler = typeHandlerRegistry.getTypeHandler(jdbcType);
174+
if (typeHandler == null) {
175+
typeHandler = ObjectTypeHandler.INSTANCE;
178176
}
179-
jdbcType = JdbcType.forCode(rsmd.getColumnType(i + 1));
180177
}
181-
typeHandler = typeHandlerRegistry.resolve(parameterObject.getClass(), javaType, jdbcType, null);
182178
}
183179
metaParam.setValue(property, typeHandler.getResult(cs, i + 1));
184180
}
@@ -586,7 +582,6 @@ private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap,
586582
private Object getPropertyMappingValue(ResultSetWrapper rsw, MetaObject metaResultObject,
587583
ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
588584
final ResultSet rs = rsw.getResultSet();
589-
final String property = propertyMapping.getProperty();
590585
if (propertyMapping.getNestedQueryId() != null) {
591586
return getNestedQueryMappingValue(rsw, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
592587
}
@@ -602,7 +597,9 @@ private Object getPropertyMappingValue(ResultSetWrapper rsw, MetaObject metaResu
602597
final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
603598
TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
604599
if (typeHandler == null) {
605-
typeHandler = resolvePropertyTypeHandler(rsw, metaResultObject, property, column);
600+
final String property = propertyMapping.getProperty();
601+
final Type javaType = property == null ? null : metaResultObject.getGenericSetterType(property).getKey();
602+
typeHandler = rsw.getTypeHandler(javaType, column);
606603
}
607604
return typeHandler.getResult(rs, column);
608605
}
@@ -621,32 +618,6 @@ private List<Object> getNestedCursorValue(ResultSetWrapper rsw, ResultMapping pr
621618
return results;
622619
}
623620

624-
private TypeHandler<?> resolvePropertyTypeHandler(ResultSetWrapper rsw, MetaObject metaResultObject,
625-
final String property, final String column) {
626-
CacheKey typeHandlerCacheKey = new CacheKey();
627-
Class<?> metaResultObjectClass = metaResultObject.getOriginalObject().getClass();
628-
typeHandlerCacheKey.update(metaResultObjectClass);
629-
typeHandlerCacheKey.update(column);
630-
typeHandlerCacheKey.update(property);
631-
return typeHandlerCache.computeIfAbsent(typeHandlerCacheKey, k -> {
632-
final JdbcType jdbcType = rsw.getJdbcType(column);
633-
final TypeHandler<?> th;
634-
if (property == null) {
635-
th = typeHandlerRegistry.getTypeHandler(jdbcType);
636-
} else {
637-
Type classToHandle = metaResultObject.getGenericSetterType(property).getKey();
638-
th = configuration.getTypeHandlerRegistry().resolve(metaResultObjectClass, classToHandle, jdbcType, null);
639-
if (th == null) {
640-
throw new TypeException(
641-
"No usable type handler found for mapping the result of column '" + column + "' to property '" + property
642-
+ "'. It was either not specified and/or could not be found for the javaType (" + classToHandle
643-
+ ") : jdbcType (" + jdbcType + ") combination.");
644-
}
645-
}
646-
return th;
647-
});
648-
}
649-
650621
private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap,
651622
MetaObject metaObject, String columnPrefix) throws SQLException {
652623
final String mapKey = resultMap.getId() + ":" + columnPrefix;
@@ -675,9 +646,7 @@ private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper
675646
continue;
676647
}
677648
final Type propertyType = metaObject.getGenericSetterType(property).getKey();
678-
Class<?> metaObjectClass = metaObject.getOriginalObject().getClass();
679-
TypeHandler<?> typeHandler = configuration.getTypeHandlerRegistry().resolve(metaObjectClass, propertyType,
680-
rsw.getJdbcType(columnName), null);
649+
TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);
681650
if (typeHandler != null) {
682651
autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler,
683652
propertyType instanceof Class && ((Class<?>) propertyType).isPrimitive()));
@@ -1089,7 +1058,7 @@ private Object prepareSimpleKeyParameter(ResultSetWrapper rsw, ResultMapping res
10891058
String columnPrefix) throws SQLException {
10901059
// parameterType is ignored in this case
10911060
final String columnName = prependPrefix(resultMapping.getColumn(), columnPrefix);
1092-
final TypeHandler<?> typeHandler = rsw.getTypeHandler(parameterType, columnName);
1061+
final TypeHandler<?> typeHandler = rsw.getTypeHandler(null, columnName);
10931062
return typeHandler.getResult(rsw.getResultSet(), columnName);
10941063
}
10951064

@@ -1101,8 +1070,8 @@ private Object prepareCompositeKeyParameter(ResultSetWrapper rsw, ResultMapping
11011070
boolean foundValues = false;
11021071
for (ResultMapping innerResultMapping : resultMapping.getComposites()) {
11031072
final String columnName = prependPrefix(innerResultMapping.getColumn(), columnPrefix);
1104-
final TypeHandler<?> typeHandler = resolvePropertyTypeHandler(rsw, metaObject, innerResultMapping.getColumn(),
1105-
columnPrefix);
1073+
final TypeHandler<?> typeHandler = rsw
1074+
.getTypeHandler(metaObject.getGenericSetterType(innerResultMapping.getProperty()).getKey(), columnName);
11061075
final Object propValue = typeHandler.getResult(rsw.getResultSet(), columnName);
11071076
// issue #353 & #560 do not execute nested query if key is null
11081077
if (propValue != null) {

src/main/java/org/apache/ibatis/executor/resultset/ResultSetWrapper.java

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.apache.ibatis.executor.resultset;
1717

18+
import java.lang.reflect.Type;
1819
import java.sql.ResultSet;
1920
import java.sql.ResultSetMetaData;
2021
import java.sql.SQLException;
@@ -27,6 +28,7 @@
2728
import java.util.Map;
2829
import java.util.Set;
2930

31+
import org.apache.ibatis.io.Resources;
3032
import org.apache.ibatis.mapping.ResultMap;
3133
import org.apache.ibatis.session.Configuration;
3234
import org.apache.ibatis.type.JdbcType;
@@ -44,7 +46,7 @@ public class ResultSetWrapper {
4446
private final List<String> columnNames = new ArrayList<>();
4547
private final List<String> classNames = new ArrayList<>();
4648
private final List<JdbcType> jdbcTypes = new ArrayList<>();
47-
private final Map<String, Map<Class<?>, TypeHandler<?>>> typeHandlerMap = new HashMap<>();
49+
private final Map<String, Map<Type, TypeHandler<?>>> typeHandlerMap = new HashMap<>();
4850
private final Map<String, Set<String>> mappedColumnNamesMap = new HashMap<>();
4951
private final Map<String, List<String>> unMappedColumnNamesMap = new HashMap<>();
5052

@@ -92,21 +94,45 @@ public JdbcType getJdbcType(String columnName) {
9294
*
9395
* @return the type handler
9496
*/
95-
public TypeHandler<?> getTypeHandler(Class<?> propertyType, String columnName) {
96-
Map<Class<?>, TypeHandler<?>> columnHandlers = typeHandlerMap.computeIfAbsent(columnName, k -> new HashMap<>());
97-
return columnHandlers.computeIfAbsent(propertyType, k -> {
98-
TypeHandler<?> handler = null;
99-
JdbcType jdbcType = getJdbcType(columnName);
100-
if (jdbcType != null && k != null) {
101-
handler = typeHandlerRegistry.getTypeHandler(k, jdbcType);
97+
public TypeHandler<?> getTypeHandler(Type propertyType, String columnName) {
98+
return typeHandlerMap.computeIfAbsent(columnName, k -> new HashMap<>()).computeIfAbsent(propertyType, k -> {
99+
int index = getColumnIndex(columnName);
100+
if (index == -1) {
101+
return ObjectTypeHandler.INSTANCE;
102102
}
103-
if (handler == null && jdbcType != null) {
103+
104+
JdbcType jdbcType = jdbcTypes.get(index);
105+
TypeHandler<?> handler = typeHandlerRegistry.resolve(null, k, jdbcType, null);
106+
if (handler != null) {
107+
return handler;
108+
}
109+
110+
Class<?> javaType = resolveClass(classNames.get(index));
111+
if (!(k instanceof Class && ((Class<?>) k).isAssignableFrom(javaType))) {
112+
// Clearly incompatible
113+
return null;
114+
}
115+
116+
handler = typeHandlerRegistry.resolve(null, javaType, jdbcType, null);
117+
if (handler == null) {
104118
handler = typeHandlerRegistry.getTypeHandler(jdbcType);
105119
}
106120
return handler == null ? ObjectTypeHandler.INSTANCE : handler;
107121
});
108122
}
109123

124+
static Class<?> resolveClass(String className) {
125+
try {
126+
// #699 className could be null
127+
if (className != null) {
128+
return Resources.classForName(className);
129+
}
130+
} catch (ClassNotFoundException e) {
131+
// ignore
132+
}
133+
return null;
134+
}
135+
110136
private int getColumnIndex(String columnName) {
111137
for (int i = 0; i < columnNames.size(); i++) {
112138
if (columnNames.get(i).equalsIgnoreCase(columnName)) {

src/main/java/org/apache/ibatis/jdbc/SqlRunner.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2009-2023 the original author or authors.
2+
* Copyright 2009-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@
2929
import java.util.Map;
3030

3131
import org.apache.ibatis.io.Resources;
32+
import org.apache.ibatis.type.ObjectTypeHandler;
3233
import org.apache.ibatis.type.TypeHandler;
3334
import org.apache.ibatis.type.TypeHandlerRegistry;
3435

@@ -242,11 +243,11 @@ private List<Map<String, Object>> getResults(ResultSet rs) throws SQLException {
242243
Class<?> type = Resources.classForName(rsmd.getColumnClassName(i + 1));
243244
TypeHandler<?> typeHandler = typeHandlerRegistry.getTypeHandler(type);
244245
if (typeHandler == null) {
245-
typeHandler = typeHandlerRegistry.getTypeHandler(Object.class);
246+
typeHandler = ObjectTypeHandler.INSTANCE;
246247
}
247248
typeHandlers.add(typeHandler);
248249
} catch (Exception e) {
249-
typeHandlers.add(typeHandlerRegistry.getTypeHandler(Object.class));
250+
typeHandlers.add(ObjectTypeHandler.INSTANCE);
250251
}
251252
}
252253
while (rs.next()) {

src/main/java/org/apache/ibatis/type/TypeHandlerRegistry.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -254,18 +254,18 @@ public TypeHandler<?> getTypeHandler(Type type, JdbcType jdbcType, Class<? exten
254254
public TypeHandler<?> getTypeHandler(Type type, JdbcType jdbcType) {
255255
if (ParamMap.class.equals(type)) {
256256
return null;
257+
} else if (type == null) {
258+
return getTypeHandler(jdbcType);
257259
}
260+
258261
TypeHandler<?> handler = null;
259262
Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = getJdbcHandlerMap(type);
260263

261264
if (Object.class.equals(type)) {
262265
if (jdbcHandlerMap != null) {
263266
handler = jdbcHandlerMap.get(jdbcType);
264267
}
265-
if (handler == null) {
266-
handler = jdbcTypeHandlerMap.get(jdbcType);
267-
}
268-
return handler != null ? handler : ObjectTypeHandler.INSTANCE;
268+
return handler;
269269
}
270270

271271
if (jdbcHandlerMap != null) {

src/test/java/org/apache/ibatis/submitted/typehandler/Mapper.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ public interface Mapper {
6161
@Select("select id from product where name = #{value}")
6262
ProductId getProductIdByName(String name);
6363

64+
@Select("select current_date d, current_time t, current_timestamp ts, localtime lt, localtimestamp lts from (values(0))")
65+
Map<String, Object> selectDateTime();
66+
6467
@Select("select id, name, released_on from product where id = #{id}")
6568
Map<String, Object> getProductAsMap(Integer id);
6669
}

src/test/java/org/apache/ibatis/submitted/typehandler/TypeHandlerTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@
1919
import static org.junit.jupiter.api.Assertions.assertNotNull;
2020

2121
import java.io.Reader;
22+
import java.sql.Time;
23+
import java.sql.Timestamp;
2224
import java.time.LocalDate;
25+
import java.time.OffsetDateTime;
26+
import java.time.OffsetTime;
2327
import java.util.Map;
2428

2529
import org.apache.ibatis.BaseDataTest;
@@ -150,6 +154,20 @@ void shouldFailIfMultipleHandlerMappedToAType() {
150154
}
151155
}
152156

157+
@Test
158+
void shouldRespectClassNameFromMetadataByDefault() {
159+
addMapper();
160+
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
161+
Mapper mapper = sqlSession.getMapper(Mapper.class);
162+
Map<String, Object> map = mapper.selectDateTime();
163+
assertEquals(java.sql.Date.class, map.get("D").getClass());
164+
assertEquals(OffsetTime.class, map.get("T").getClass());
165+
assertEquals(OffsetDateTime.class, map.get("TS").getClass());
166+
assertEquals(Time.class, map.get("LT").getClass());
167+
assertEquals(Timestamp.class, map.get("LTS").getClass());
168+
}
169+
}
170+
153171
@Test
154172
void shouldHandlerBePickedBasedOnRuntimeJdbcType() {
155173
sqlSessionFactory.getConfiguration().getTypeHandlerRegistry().register(ProductId.class, null,

0 commit comments

Comments
 (0)