Skip to content

Commit de9602e

Browse files
committed
Resolve resultType by namespace and id when not provide resultType and resultMap
1 parent 35262b0 commit de9602e

File tree

9 files changed

+334
-67
lines changed

9 files changed

+334
-67
lines changed

src/main/java/org/apache/ibatis/builder/MapperBuilderAssistant.java

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,29 @@
1515
*/
1616
package org.apache.ibatis.builder;
1717

18+
import java.lang.reflect.Array;
19+
import java.lang.reflect.GenericArrayType;
20+
import java.lang.reflect.Method;
21+
import java.lang.reflect.ParameterizedType;
22+
import java.lang.reflect.Type;
1823
import java.util.ArrayList;
24+
import java.util.Collection;
1925
import java.util.Collections;
2026
import java.util.HashMap;
2127
import java.util.HashSet;
2228
import java.util.List;
2329
import java.util.Map;
30+
import java.util.Optional;
2431
import java.util.Properties;
2532
import java.util.Set;
2633
import java.util.StringTokenizer;
2734

35+
import org.apache.ibatis.annotations.MapKey;
36+
import org.apache.ibatis.annotations.ResultType;
2837
import org.apache.ibatis.cache.Cache;
2938
import org.apache.ibatis.cache.decorators.LruCache;
3039
import org.apache.ibatis.cache.impl.PerpetualCache;
40+
import org.apache.ibatis.cursor.Cursor;
3141
import org.apache.ibatis.executor.ErrorContext;
3242
import org.apache.ibatis.executor.keygen.KeyGenerator;
3343
import org.apache.ibatis.mapping.CacheBuilder;
@@ -44,6 +54,7 @@
4454
import org.apache.ibatis.mapping.SqlSource;
4555
import org.apache.ibatis.mapping.StatementType;
4656
import org.apache.ibatis.reflection.MetaClass;
57+
import org.apache.ibatis.reflection.TypeParameterResolver;
4758
import org.apache.ibatis.scripting.LanguageDriver;
4859
import org.apache.ibatis.session.Configuration;
4960
import org.apache.ibatis.type.JdbcType;
@@ -570,4 +581,61 @@ private Class<?> resolveParameterJavaType(Class<?> resultType, String property,
570581
return javaType;
571582
}
572583

584+
public Class<?> getReturnType(Method method, Class<?> type) {
585+
Class<?> returnType = method.getReturnType();
586+
Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, type);
587+
if (resolvedReturnType instanceof Class) {
588+
returnType = (Class<?>) resolvedReturnType;
589+
if (returnType.isArray()) {
590+
returnType = returnType.getComponentType();
591+
}
592+
// gcode issue #508
593+
if (void.class.equals(returnType)) {
594+
ResultType rt = method.getAnnotation(ResultType.class);
595+
if (rt != null) {
596+
returnType = rt.value();
597+
}
598+
}
599+
} else if (resolvedReturnType instanceof ParameterizedType) {
600+
ParameterizedType parameterizedType = (ParameterizedType) resolvedReturnType;
601+
Class<?> rawType = (Class<?>) parameterizedType.getRawType();
602+
if (Collection.class.isAssignableFrom(rawType) || Cursor.class.isAssignableFrom(rawType)) {
603+
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
604+
if (actualTypeArguments != null && actualTypeArguments.length == 1) {
605+
Type returnTypeParameter = actualTypeArguments[0];
606+
if (returnTypeParameter instanceof Class<?>) {
607+
returnType = (Class<?>) returnTypeParameter;
608+
} else if (returnTypeParameter instanceof ParameterizedType) {
609+
// (gcode issue #443) actual type can be a also a parameterized type
610+
returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();
611+
} else if (returnTypeParameter instanceof GenericArrayType) {
612+
Class<?> componentType = (Class<?>) ((GenericArrayType) returnTypeParameter).getGenericComponentType();
613+
// (gcode issue #525) support List<byte[]>
614+
returnType = Array.newInstance(componentType, 0).getClass();
615+
}
616+
}
617+
} else if (method.isAnnotationPresent(MapKey.class) && Map.class.isAssignableFrom(rawType)) {
618+
// (gcode issue 504) Do not look into Maps if there is not MapKey annotation
619+
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
620+
if (actualTypeArguments != null && actualTypeArguments.length == 2) {
621+
Type returnTypeParameter = actualTypeArguments[1];
622+
if (returnTypeParameter instanceof Class<?>) {
623+
returnType = (Class<?>) returnTypeParameter;
624+
} else if (returnTypeParameter instanceof ParameterizedType) {
625+
// (gcode issue 443) actual type can be a also a parameterized type
626+
returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();
627+
}
628+
}
629+
} else if (Optional.class.equals(rawType)) {
630+
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
631+
Type returnTypeParameter = actualTypeArguments[0];
632+
if (returnTypeParameter instanceof Class<?>) {
633+
returnType = (Class<?>) returnTypeParameter;
634+
}
635+
}
636+
}
637+
638+
return returnType;
639+
}
640+
573641
}

src/main/java/org/apache/ibatis/builder/annotation/MapperAnnotationBuilder.java

Lines changed: 2 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,7 @@
1818
import java.io.IOException;
1919
import java.io.InputStream;
2020
import java.lang.annotation.Annotation;
21-
import java.lang.reflect.Array;
22-
import java.lang.reflect.GenericArrayType;
2321
import java.lang.reflect.Method;
24-
import java.lang.reflect.ParameterizedType;
25-
import java.lang.reflect.Type;
2622
import java.util.ArrayList;
2723
import java.util.Arrays;
2824
import java.util.Collection;
@@ -45,13 +41,11 @@
4541
import org.apache.ibatis.annotations.Insert;
4642
import org.apache.ibatis.annotations.InsertProvider;
4743
import org.apache.ibatis.annotations.Lang;
48-
import org.apache.ibatis.annotations.MapKey;
4944
import org.apache.ibatis.annotations.Options;
5045
import org.apache.ibatis.annotations.Options.FlushCachePolicy;
5146
import org.apache.ibatis.annotations.Property;
5247
import org.apache.ibatis.annotations.Result;
5348
import org.apache.ibatis.annotations.ResultMap;
54-
import org.apache.ibatis.annotations.ResultType;
5549
import org.apache.ibatis.annotations.Results;
5650
import org.apache.ibatis.annotations.Select;
5751
import org.apache.ibatis.annotations.SelectKey;
@@ -65,7 +59,6 @@
6559
import org.apache.ibatis.builder.IncompleteElementException;
6660
import org.apache.ibatis.builder.MapperBuilderAssistant;
6761
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
68-
import org.apache.ibatis.cursor.Cursor;
6962
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
7063
import org.apache.ibatis.executor.keygen.KeyGenerator;
7164
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
@@ -81,7 +74,6 @@
8174
import org.apache.ibatis.mapping.SqlSource;
8275
import org.apache.ibatis.mapping.StatementType;
8376
import org.apache.ibatis.parsing.PropertyParser;
84-
import org.apache.ibatis.reflection.TypeParameterResolver;
8577
import org.apache.ibatis.scripting.LanguageDriver;
8678
import org.apache.ibatis.session.Configuration;
8779
import org.apache.ibatis.session.ResultHandler;
@@ -224,7 +216,7 @@ private void parseCacheRef() {
224216
}
225217

226218
private String parseResultMap(Method method) {
227-
Class<?> returnType = getReturnType(method);
219+
Class<?> returnType = assistant.getReturnType(method, type);
228220
Arg[] args = method.getAnnotationsByType(Arg.class);
229221
Result[] results = method.getAnnotationsByType(Result.class);
230222
TypeDiscriminator typeDiscriminator = method.getAnnotation(TypeDiscriminator.class);
@@ -366,7 +358,7 @@ void parseStatement(Method method) {
366358
null,
367359
parameterTypeClass,
368360
resultMapId,
369-
getReturnType(method),
361+
assistant.getReturnType(method, type),
370362
resultSetType,
371363
flushCache,
372364
useCache,
@@ -408,63 +400,6 @@ private Class<?> getParameterType(Method method) {
408400
return parameterType;
409401
}
410402

411-
private Class<?> getReturnType(Method method) {
412-
Class<?> returnType = method.getReturnType();
413-
Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, type);
414-
if (resolvedReturnType instanceof Class) {
415-
returnType = (Class<?>) resolvedReturnType;
416-
if (returnType.isArray()) {
417-
returnType = returnType.getComponentType();
418-
}
419-
// gcode issue #508
420-
if (void.class.equals(returnType)) {
421-
ResultType rt = method.getAnnotation(ResultType.class);
422-
if (rt != null) {
423-
returnType = rt.value();
424-
}
425-
}
426-
} else if (resolvedReturnType instanceof ParameterizedType) {
427-
ParameterizedType parameterizedType = (ParameterizedType) resolvedReturnType;
428-
Class<?> rawType = (Class<?>) parameterizedType.getRawType();
429-
if (Collection.class.isAssignableFrom(rawType) || Cursor.class.isAssignableFrom(rawType)) {
430-
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
431-
if (actualTypeArguments != null && actualTypeArguments.length == 1) {
432-
Type returnTypeParameter = actualTypeArguments[0];
433-
if (returnTypeParameter instanceof Class<?>) {
434-
returnType = (Class<?>) returnTypeParameter;
435-
} else if (returnTypeParameter instanceof ParameterizedType) {
436-
// (gcode issue #443) actual type can be a also a parameterized type
437-
returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();
438-
} else if (returnTypeParameter instanceof GenericArrayType) {
439-
Class<?> componentType = (Class<?>) ((GenericArrayType) returnTypeParameter).getGenericComponentType();
440-
// (gcode issue #525) support List<byte[]>
441-
returnType = Array.newInstance(componentType, 0).getClass();
442-
}
443-
}
444-
} else if (method.isAnnotationPresent(MapKey.class) && Map.class.isAssignableFrom(rawType)) {
445-
// (gcode issue 504) Do not look into Maps if there is not MapKey annotation
446-
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
447-
if (actualTypeArguments != null && actualTypeArguments.length == 2) {
448-
Type returnTypeParameter = actualTypeArguments[1];
449-
if (returnTypeParameter instanceof Class<?>) {
450-
returnType = (Class<?>) returnTypeParameter;
451-
} else if (returnTypeParameter instanceof ParameterizedType) {
452-
// (gcode issue 443) actual type can be a also a parameterized type
453-
returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();
454-
}
455-
}
456-
} else if (Optional.class.equals(rawType)) {
457-
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
458-
Type returnTypeParameter = actualTypeArguments[0];
459-
if (returnTypeParameter instanceof Class<?>) {
460-
returnType = (Class<?>) returnTypeParameter;
461-
}
462-
}
463-
}
464-
465-
return returnType;
466-
}
467-
468403
private void applyResults(Result[] results, Class<?> resultType, List<ResultMapping> resultMappings) {
469404
for (Result result : results) {
470405
List<ResultFlag> flags = new ArrayList<>();

src/main/java/org/apache/ibatis/builder/xml/XMLStatementBuilder.java

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

18+
import java.lang.reflect.Method;
1819
import java.util.List;
1920
import java.util.Locale;
2021

@@ -101,6 +102,10 @@ public void parseStatementNode() {
101102
String resultType = context.getStringAttribute("resultType");
102103
Class<?> resultTypeClass = resolveClass(resultType);
103104
String resultMap = context.getStringAttribute("resultMap");
105+
if (resultTypeClass == null && resultMap == null) {
106+
// Resolve resultType by namespace and id, if not provide resultType and resultMap
107+
resultTypeClass = resolveResultType(id);
108+
}
104109
String resultSetType = context.getStringAttribute("resultSetType");
105110
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
106111
if (resultSetTypeEnum == null) {
@@ -199,4 +204,34 @@ private LanguageDriver getLanguageDriver(String lang) {
199204
return configuration.getLanguageDriver(langClass);
200205
}
201206

207+
private Class<?> resolveResultType(String id) {
208+
String namespace = builderAssistant.getCurrentNamespace();
209+
Class<?> type;
210+
try {
211+
type = resolveClass(namespace);
212+
if (type == null) {
213+
return null;
214+
}
215+
} catch (Exception e) {
216+
// ignore
217+
return null;
218+
}
219+
Method mapperMethod = findMapperMethod(type, id);
220+
if (mapperMethod != null) {
221+
return builderAssistant.getReturnType(mapperMethod, type);
222+
}
223+
return null;
224+
}
225+
226+
private Method findMapperMethod(Class<?> type, String methodName) {
227+
Method[] methods = type.getMethods();
228+
Method foundMethod = null;
229+
for (Method method : methods) {
230+
if (method.getName().equals(methodName) && !method.isBridge() && !method.isDefault()) {
231+
foundMethod = method;
232+
break;
233+
}
234+
}
235+
return foundMethod;
236+
}
202237
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2009-2022 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.submitted.no_result_type_map;
17+
18+
import org.apache.ibatis.annotations.Param;
19+
20+
import java.util.List;
21+
22+
public interface Mapper {
23+
24+
User getUser(@Param("id") Integer id);
25+
26+
List<User> getAllUsers();
27+
28+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright 2009-2022 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.submitted.no_result_type_map;
17+
18+
import java.io.Reader;
19+
import java.util.List;
20+
21+
import org.apache.ibatis.BaseDataTest;
22+
import org.apache.ibatis.io.Resources;
23+
import org.apache.ibatis.session.SqlSession;
24+
import org.apache.ibatis.session.SqlSessionFactory;
25+
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
26+
import org.junit.jupiter.api.Assertions;
27+
import org.junit.jupiter.api.BeforeAll;
28+
import org.junit.jupiter.api.Test;
29+
30+
class NoResultTypeMapTest {
31+
32+
private static SqlSessionFactory sqlSessionFactory;
33+
34+
@BeforeAll
35+
static void setUp() throws Exception {
36+
// create a SqlSessionFactory
37+
try (Reader reader = Resources
38+
.getResourceAsReader("org/apache/ibatis/submitted/no_result_type_map/mybatis-config.xml")) {
39+
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
40+
sqlSessionFactory.getConfiguration().addMapper(Mapper.class);
41+
}
42+
43+
// populate in-memory database
44+
BaseDataTest.runScript(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(),
45+
"org/apache/ibatis/submitted/no_result_type_map/CreateDB.sql");
46+
}
47+
48+
@Test
49+
void shouldGetAUser() {
50+
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
51+
Mapper mapper = sqlSession.getMapper(Mapper.class);
52+
User user = mapper.getUser(1);
53+
Assertions.assertEquals("User1", user.getName());
54+
}
55+
}
56+
57+
@Test
58+
void shouldGetAllUsers() {
59+
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
60+
Mapper mapper = sqlSession.getMapper(Mapper.class);
61+
List<User> users = mapper.getAllUsers();
62+
Assertions.assertEquals(3, users.size());
63+
}
64+
}
65+
66+
}

0 commit comments

Comments
 (0)