Skip to content

Commit 2d50c4b

Browse files
authored
Merge pull request #2767 from FlyInWind1/no-result-type
Resolve resultType by namespace and id when not provide resultType and resultMap
2 parents 8e4cf55 + d51f3a7 commit 2d50c4b

File tree

10 files changed

+316
-8
lines changed

10 files changed

+316
-8
lines changed

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

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2009-2022 the original author or authors.
2+
* Copyright 2009-2023 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.
@@ -138,7 +138,7 @@ public void parse() {
138138
parsePendingMethods();
139139
}
140140

141-
private boolean canHaveStatement(Method method) {
141+
private static boolean canHaveStatement(Method method) {
142142
// issue #237
143143
return !method.isBridge() && !method.isDefault();
144144
}
@@ -224,7 +224,7 @@ private void parseCacheRef() {
224224
}
225225

226226
private String parseResultMap(Method method) {
227-
Class<?> returnType = getReturnType(method);
227+
Class<?> returnType = getReturnType(method, type);
228228
Arg[] args = method.getAnnotationsByType(Arg.class);
229229
Result[] results = method.getAnnotationsByType(Result.class);
230230
TypeDiscriminator typeDiscriminator = method.getAnnotation(TypeDiscriminator.class);
@@ -366,7 +366,7 @@ void parseStatement(Method method) {
366366
null,
367367
parameterTypeClass,
368368
resultMapId,
369-
getReturnType(method),
369+
getReturnType(method, type),
370370
resultSetType,
371371
flushCache,
372372
useCache,
@@ -408,7 +408,7 @@ private Class<?> getParameterType(Method method) {
408408
return parameterType;
409409
}
410410

411-
private Class<?> getReturnType(Method method) {
411+
private static Class<?> getReturnType(Method method, Class<?> type) {
412412
Class<?> returnType = method.getReturnType();
413413
Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, type);
414414
if (resolvedReturnType instanceof Class) {
@@ -669,6 +669,23 @@ private Optional<AnnotationWrapper> getAnnotationWrapper(Method method, boolean
669669
return Optional.ofNullable(annotationWrapper);
670670
}
671671

672+
public static Class<?> getMethodReturnType(String mapperFqn, String localStatementId) {
673+
if (mapperFqn == null || localStatementId == null) {
674+
return null;
675+
}
676+
try {
677+
Class<?> mapperClass = Resources.classForName(mapperFqn);
678+
for (Method method : mapperClass.getMethods()) {
679+
if (method.getName().equals(localStatementId) && canHaveStatement(method)) {
680+
return getReturnType(method, mapperClass);
681+
}
682+
}
683+
} catch (ClassNotFoundException e) {
684+
// No corresponding mapper interface which is OK
685+
}
686+
return null;
687+
}
688+
672689
private class AnnotationWrapper {
673690
private final Annotation annotation;
674691
private final String databaseId;

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2009-2022 the original author or authors.
2+
* Copyright 2009-2023 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.
@@ -20,6 +20,7 @@
2020

2121
import org.apache.ibatis.builder.BaseBuilder;
2222
import org.apache.ibatis.builder.MapperBuilderAssistant;
23+
import org.apache.ibatis.builder.annotation.MapperAnnotationBuilder;
2324
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
2425
import org.apache.ibatis.executor.keygen.KeyGenerator;
2526
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
@@ -101,6 +102,9 @@ 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+
resultTypeClass = MapperAnnotationBuilder.getMethodReturnType(builderAssistant.getCurrentNamespace(), id);
107+
}
104108
String resultSetType = context.getStringAttribute("resultSetType");
105109
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
106110
if (resultSetTypeEnum == null) {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2009-2022 the original author or authors.
2+
* Copyright 2009-2023 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.
@@ -294,7 +294,7 @@ private void cleanUpAfterHandlingResultSet() {
294294
private void validateResultMapsCount(ResultSetWrapper rsw, int resultMapCount) {
295295
if (rsw != null && resultMapCount < 1) {
296296
throw new ExecutorException("A query was run and no Result Maps were found for the Mapped Statement '" + mappedStatement.getId()
297-
+ "'. It's likely that neither a Result Type nor a Result Map was specified.");
297+
+ "'. 'resultType' or 'resultMap' must be specified when there is no corresponding method.");
298298
}
299299
}
300300

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2009-2023 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 extends ParentMapper<User>{
23+
24+
User getUser(@Param("id") Integer id);
25+
26+
List<User> getAllUsers();
27+
28+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright 2009-2023 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 static org.junit.jupiter.api.Assertions.*;
19+
20+
import java.io.Reader;
21+
import java.util.List;
22+
23+
import org.apache.ibatis.BaseDataTest;
24+
import org.apache.ibatis.exceptions.PersistenceException;
25+
import org.apache.ibatis.executor.ExecutorException;
26+
import org.apache.ibatis.io.Resources;
27+
import org.apache.ibatis.session.SqlSession;
28+
import org.apache.ibatis.session.SqlSessionFactory;
29+
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
30+
import org.junit.jupiter.api.Assertions;
31+
import org.junit.jupiter.api.BeforeAll;
32+
import org.junit.jupiter.api.Test;
33+
34+
class NoResultTypeMapTest {
35+
36+
private static SqlSessionFactory sqlSessionFactory;
37+
38+
@BeforeAll
39+
static void setUp() throws Exception {
40+
// create a SqlSessionFactory
41+
try (Reader reader = Resources
42+
.getResourceAsReader("org/apache/ibatis/submitted/no_result_type_map/mybatis-config.xml")) {
43+
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
44+
sqlSessionFactory.getConfiguration().addMapper(Mapper.class);
45+
}
46+
47+
// populate in-memory database
48+
BaseDataTest.runScript(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(),
49+
"org/apache/ibatis/submitted/no_result_type_map/CreateDB.sql");
50+
}
51+
52+
@Test
53+
void shouldGetAUser() {
54+
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
55+
Mapper mapper = sqlSession.getMapper(Mapper.class);
56+
User user = mapper.getUser(1);
57+
Assertions.assertEquals("User1", user.getName());
58+
}
59+
}
60+
61+
@Test
62+
void shouldGetAllUsers() {
63+
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
64+
Mapper mapper = sqlSession.getMapper(Mapper.class);
65+
List<User> users = mapper.getAllUsers();
66+
Assertions.assertEquals(3, users.size());
67+
}
68+
}
69+
70+
@Test
71+
void shouldResolveInheritedReturnType() {
72+
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
73+
Mapper mapper = sqlSession.getMapper(Mapper.class);
74+
List<User> users = mapper.getAllUsersInParent();
75+
Assertions.assertEquals(3, users.size());
76+
}
77+
}
78+
79+
@Test
80+
void shouldFailIfNoMatchingMethod() {
81+
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
82+
PersistenceException ex = assertThrows(PersistenceException.class,
83+
() -> sqlSession.selectList("org.apache.ibatis.submitted.no_result_type_map.Mapper.noMatchingMethod"));
84+
ExecutorException cause = (ExecutorException) ex.getCause();
85+
assertEquals("A query was run and no Result Maps were found for the Mapped Statement "
86+
+ "'org.apache.ibatis.submitted.no_result_type_map.Mapper.noMatchingMethod'. "
87+
+ "'resultType' or 'resultMap' must be specified when there is no corresponding method.",
88+
cause.getMessage());
89+
}
90+
}
91+
92+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright 2009-2023 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.util.List;
19+
20+
public interface ParentMapper<T> {
21+
22+
List<T> getAllUsersInParent();
23+
24+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 2009-2023 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+
public class User {
19+
20+
private Integer id;
21+
private String name;
22+
23+
public Integer getId() {
24+
return id;
25+
}
26+
27+
public void setId(Integer id) {
28+
this.id = id;
29+
}
30+
31+
public String getName() {
32+
return name;
33+
}
34+
35+
public void setName(String name) {
36+
this.name = name;
37+
}
38+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--
2+
-- Copyright 2009-2023 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+
17+
drop table users if exists;
18+
19+
create table users (
20+
id int,
21+
name varchar(20)
22+
);
23+
24+
insert into users (id, name) values(1, 'User1');
25+
insert into users (id, name) values(2, 'User2');
26+
insert into users (id, name) values(3, 'User3');
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
Copyright 2009-2023 the original author or authors.
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
https://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
18+
-->
19+
<!DOCTYPE mapper
20+
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
21+
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
22+
23+
<mapper namespace="org.apache.ibatis.submitted.no_result_type_map.Mapper">
24+
25+
<select id="getUser">
26+
select * from users where id = #{id}
27+
</select>
28+
29+
<select id="getAllUsers">
30+
select * from users
31+
</select>
32+
33+
<select id="getAllUsersInParent">
34+
select * from users
35+
</select>
36+
37+
<select id="noMatchingMethod">
38+
select * from users
39+
</select>
40+
41+
</mapper>

0 commit comments

Comments
 (0)