Skip to content

Commit 23db07b

Browse files
committed
Fix for http://code.google.com/p/mybatis/issues/detail?id=15 : add resultMap/select attribute to constructor/arg element. Patch contributed by Tim Chen and "Gus4" under the Apache 2 license terms.
1 parent 306634e commit 23db07b

File tree

9 files changed

+179
-33
lines changed

9 files changed

+179
-33
lines changed

src/main/java/org/apache/ibatis/annotations/Arg.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,8 @@
2121
public abstract JdbcType jdbcType() default JdbcType.UNDEFINED;
2222

2323
public abstract Class<? extends TypeHandler> typeHandler() default TypeHandler.class;
24+
25+
public abstract String select() default "";
26+
27+
public abstract String resultMap() default "";
2428
}

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

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {
8888

8989
public void parse() {
9090
String resource = type.toString();
91-
if (!configuration.isResourceLoaded(resource)) {
91+
if (!configuration.isResourceLoaded(resource)) {
9292
loadXmlResource();
9393
configuration.addLoadedResource(resource);
9494
assistant.setCurrentNamespace(type.getName());
@@ -228,7 +228,7 @@ private void parseStatement(Method method) {
228228
StatementType statementType = StatementType.PREPARED;
229229
ResultSetType resultSetType = ResultSetType.FORWARD_ONLY;
230230
SqlCommandType sqlCommandType = getSqlCommandType(method);
231-
231+
232232
KeyGenerator keyGenerator;
233233
String keyProperty = "id";
234234
if (SqlCommandType.INSERT.equals(sqlCommandType)) {
@@ -248,7 +248,7 @@ private void parseStatement(Method method) {
248248
} else {
249249
keyGenerator = new NoKeyGenerator();
250250
}
251-
251+
252252
if (options != null) {
253253
flushCache = options.flushCache();
254254
useCache = options.useCache();
@@ -257,15 +257,15 @@ private void parseStatement(Method method) {
257257
statementType = options.statementType();
258258
resultSetType = options.resultSetType();
259259
}
260-
260+
261261
ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
262262
String resultMapId;
263263
if (resultMapAnnotation == null) {
264264
resultMapId = generateResultMapName(method);
265265
} else {
266266
resultMapId = resultMapAnnotation.value();
267267
}
268-
268+
269269
assistant.addMappedStatement(
270270
mappedStatementId,
271271
sqlSource,
@@ -362,7 +362,7 @@ private SqlSource buildSqlSourceFromStrings(String[] strings) {
362362
MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
363363
return new DynamicSqlSource(configuration, rootSqlNode);
364364
}
365-
365+
366366
private SqlCommandType getSqlCommandType(Method method) {
367367
Class<? extends Annotation> type = getSqlAnnotationType(method);
368368

@@ -455,8 +455,8 @@ private void applyConstructorArgs(Arg[] args, Class<?> resultType, List<ResultMa
455455
arg.column(),
456456
arg.javaType() == void.class ? null : arg.javaType(),
457457
arg.jdbcType() == JdbcType.UNDEFINED ? null : arg.jdbcType(),
458-
null,
459-
null,
458+
nullOrEmpty(arg.select()),
459+
nullOrEmpty(arg.resultMap()),
460460
null,
461461
arg.typeHandler() == TypeHandler.class ? null : arg.typeHandler(),
462462
flags);
@@ -465,7 +465,11 @@ private void applyConstructorArgs(Arg[] args, Class<?> resultType, List<ResultMa
465465
}
466466
}
467467

468-
private Result[] resultsIf(Results results) {
468+
private String nullOrEmpty(String value) {
469+
return value == null || value.trim().length() == 0 ? null : value;
470+
}
471+
472+
private Result[] resultsIf(Results results) {
469473
return results == null ? new Result[0] : results.value();
470474
}
471475

@@ -500,7 +504,7 @@ private KeyGenerator handleSelectKeyAnnotation(SelectKey selectKeyAnnotation, St
500504
id = assistant.applyCurrentNamespace(id);
501505

502506
MappedStatement keyStatement = configuration.getMappedStatement(id, false);
503-
SelectKeyGenerator answer = new SelectKeyGenerator(keyStatement, executeBefore);
507+
SelectKeyGenerator answer = new SelectKeyGenerator(keyStatement, executeBefore);
504508
configuration.addKeyGenerator(id, answer);
505509
return answer;
506510
}

src/main/java/org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ javaType CDATA #IMPLIED
7878
column CDATA #IMPLIED
7979
jdbcType CDATA #IMPLIED
8080
typeHandler CDATA #IMPLIED
81+
select CDATA #IMPLIED
82+
resultMap CDATA #IMPLIED
8183
>
8284

8385
<!ELEMENT collection (constructor?,id*,result*,association*,collection*, discriminator?)>

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

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ public List handleResultSets(Statement stmt) throws SQLException {
105105
}
106106
}
107107
}
108-
108+
109109
validateResultMapsCount(rs, resultMapCount);
110110
while (rs != null && resultMapCount > resultSetCount) {
111111
final ResultMap resultMap = resultMaps.get(resultSetCount);
@@ -126,7 +126,7 @@ protected void closeResultSet(ResultSet rs) {
126126
// ignore
127127
}
128128
}
129-
129+
130130
protected void cleanUpAfterHandlingResultSet() {
131131
}
132132

@@ -339,9 +339,20 @@ protected Object createParameterizedResultObject(ResultSet rs, Class resultType,
339339
boolean foundValues = false;
340340
for (ResultMapping constructorMapping : constructorMappings) {
341341
final Class parameterType = constructorMapping.getJavaType();
342-
final TypeHandler typeHandler = constructorMapping.getTypeHandler();
343342
final String column = constructorMapping.getColumn();
344-
final Object value = typeHandler.getResult(rs, column);
343+
final Object value;
344+
// check for nested query
345+
if (constructorMapping.getNestedQueryId() != null) {
346+
value = getNestedQueryConstructorValue(rs, constructorMapping);
347+
} else if(constructorMapping.getNestedResultMapId() != null) {
348+
final ResultMap resultMap = configuration.getResultMap(constructorMapping.getNestedResultMapId());
349+
final ResultLoaderMap lazyLoader = instantiateResultLoaderMap();
350+
value = createResultObject(rs, resultMap, lazyLoader);
351+
}else {
352+
// get simple result
353+
final TypeHandler typeHandler = constructorMapping.getTypeHandler();
354+
value = typeHandler.getResult(rs, column);
355+
}
345356
constructorArgTypes.add(parameterType);
346357
constructorArgs.add(value);
347358
foundValues = value != null || foundValues;
@@ -368,6 +379,19 @@ protected Object createPrimitiveResultObject(ResultSet rs, ResultMap resultMap)
368379
// NESTED QUERY
369380
//
370381

382+
protected Object getNestedQueryConstructorValue(ResultSet rs, ResultMapping constructorMapping) throws SQLException {
383+
final String nestedQueryId = constructorMapping.getNestedQueryId();
384+
final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
385+
final Class nestedQueryParameterType = nestedQuery.getParameterMap().getType();
386+
final Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, constructorMapping, nestedQueryParameterType);
387+
Object value = null;
388+
if (nestedQueryParameterObject != null) {
389+
final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, constructorMapping.getJavaType());
390+
value = resultLoader.loadResult();
391+
}
392+
return value;
393+
}
394+
371395
protected Object getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader) throws SQLException {
372396
final String nestedQueryId = propertyMapping.getNestedQueryId();
373397
final String property = propertyMapping.getProperty();
@@ -463,4 +487,4 @@ protected Object getDiscriminatorValue(ResultSet rs, Discriminator discriminator
463487
}
464488
}
465489

466-
}
490+
}

src/test/java/domain/blog/Blog.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,17 @@ public class Blog {
99
private Author author;
1010
private List<Post> posts;
1111

12+
public Blog() {
13+
}
14+
15+
public Blog(int id, String title, Author author, List<Post> posts) {
16+
this();
17+
this.id = id;
18+
this.title = title;
19+
this.author = author;
20+
this.posts = posts;
21+
}
22+
1223
public int getId() {
1324
return id;
1425
}

src/test/java/org/apache/ibatis/binding/BindingTest.java

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import org.junit.Test;
1111

1212
import java.util.*;
13-
import org.junit.Ignore;
1413

1514
public class BindingTest {
1615
private static SqlSessionFactory sqlSessionFactory;
@@ -294,6 +293,54 @@ public void shouldExecuteBoundSelectOneBlogStatement() {
294293
}
295294
}
296295

296+
@Test
297+
public void shouldExecuteBoundSelectOneBlogStatementWithConstructor() {
298+
SqlSession session = sqlSessionFactory.openSession();
299+
try {
300+
BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
301+
Blog blog = mapper.selectBlogUsingConstructor(1);
302+
assertEquals(1, blog.getId());
303+
assertEquals("Jim Business", blog.getTitle());
304+
assertNotNull("author should not be null", blog.getAuthor());
305+
List<Post> posts = blog.getPosts();
306+
assertTrue("posts should not be empty", posts != null && !posts.isEmpty());
307+
} finally {
308+
session.close();
309+
}
310+
}
311+
312+
@Test
313+
public void shouldExecuteBoundSelectBlogUsingConstructorWithResultMap() {
314+
SqlSession session = sqlSessionFactory.openSession();
315+
try {
316+
BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
317+
Blog blog = mapper.selectBlogUsingConstructorWithResultMap(1);
318+
assertEquals(1, blog.getId());
319+
assertEquals("Jim Business", blog.getTitle());
320+
assertNotNull("author should not be null", blog.getAuthor());
321+
List<Post> posts = blog.getPosts();
322+
assertTrue("posts should not be empty", posts != null && !posts.isEmpty());
323+
} finally {
324+
session.close();
325+
}
326+
}
327+
328+
@Test
329+
public void shouldExecuteBoundSelectOneBlogStatementWithConstructorUsingXMLConfig() {
330+
SqlSession session = sqlSessionFactory.openSession();
331+
try {
332+
BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
333+
Blog blog = mapper.selectBlogByIdUsingConstructor(1);
334+
assertEquals(1, blog.getId());
335+
assertEquals("Jim Business", blog.getTitle());
336+
assertNotNull("author should not be null", blog.getAuthor());
337+
List<Post> posts = blog.getPosts();
338+
assertTrue("posts should not be empty", posts != null && !posts.isEmpty());
339+
} finally {
340+
session.close();
341+
}
342+
}
343+
297344
@Test
298345
public void shouldSelectOneBlogAsMap() {
299346
SqlSession session = sqlSessionFactory.openSession();

src/test/java/org/apache/ibatis/binding/BoundAuthorMapper.xml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,17 @@
66

77
<mapper namespace="org.apache.ibatis.binding.BoundAuthorMapper">
88

9+
<resultMap id="authorResultMap" type="domain.blog.Author">
10+
<constructor>
11+
<idArg column="author_id" javaType="java.lang.Integer"/>
12+
<arg column="author_username" javaType="java.lang.String"/>
13+
<arg column="author_password" javaType="java.lang.String"/>
14+
<arg column="author_email" javaType="java.lang.String"/>
15+
<arg column="author_bio" javaType="java.lang.String"/>
16+
<arg column="favourite_section" javaType="domain.blog.Section"/>
17+
</constructor>
18+
</resultMap>
19+
920
<insert id="insertAuthor" parameterType="domain.blog.Author">
1021
<selectKey keyProperty="id" resultType="int" order="BEFORE">
1122
select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1
@@ -32,4 +43,4 @@
3243
</select>
3344

3445

35-
</mapper>
46+
</mapper>

src/test/java/org/apache/ibatis/binding/BoundBlogMapper.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.apache.ibatis.binding;
22

3+
import domain.blog.Author;
34
import domain.blog.Blog;
45
import domain.blog.DraftPost;
56
import domain.blog.Post;
@@ -83,6 +84,22 @@ public interface BoundBlogMapper {
8384

8485
//======================================================
8586

87+
@Select("SELECT * FROM " +
88+
"blog WHERE id = #{id}")
89+
@ConstructorArgs({
90+
@Arg(column = "id", javaType = int.class, id = true),
91+
@Arg(column = "title", javaType = String.class),
92+
@Arg(column = "author_id", javaType = Author.class, select = "org.apache.ibatis.binding.BoundAuthorMapper.selectAuthor"),
93+
@Arg(column = "id", javaType = List.class, select = "selectPostsForBlog")
94+
})
95+
Blog selectBlogUsingConstructor(int id);
96+
97+
Blog selectBlogUsingConstructorWithResultMap(int i);
98+
99+
Blog selectBlogByIdUsingConstructor(int id);
100+
101+
//======================================================
102+
86103
@Select("SELECT * FROM " +
87104
"blog WHERE id = #{id}")
88105
Map selectBlogAsMap(Map params);
Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,66 @@
11
<?xml version="1.0" encoding="UTF-8" ?>
22

33
<!DOCTYPE mapper
4-
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
5-
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
4+
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
5+
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
66

77
<mapper namespace="org.apache.ibatis.binding.BoundBlogMapper">
88

9-
<select id="selectRandom" resultType="int">
9+
<select id="selectRandom" resultType="int">
1010
select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1
1111
</select>
1212

13-
<select id="selectBlogsFromXML" resultType="domain.blog.Blog">
13+
<select id="selectBlogsFromXML" resultType="domain.blog.Blog">
1414
SELECT * FROM blog
1515
</select>
1616

17-
<resultMap id="blogWithPosts" type="Blog">
18-
<id property="id" column="id"/>
19-
<result property="title" column="title"/>
20-
<association property="author" column="author_id"
21-
select="selectAuthorWithInlineParams"/>
22-
<collection property="posts" column="id" select="selectPostsForBlog"/>
23-
</resultMap>
17+
<resultMap id="blogWithPosts" type="Blog">
18+
<id property="id" column="id"/>
19+
<result property="title" column="title"/>
20+
<association property="author" column="author_id"
21+
select="selectAuthorWithInlineParams"/>
22+
<collection property="posts" column="id" select="selectPostsForBlog"/>
23+
</resultMap>
2424

25-
<select id="selectBlogWithPostsUsingSubSelect" parameterType="int" resultMap="blogWithPosts">
25+
<resultMap id="blogUsingConstructor" type="Blog">
26+
<constructor>
27+
<idArg column="id" javaType="_int"/>
28+
<arg column="title" javaType="java.lang.String"/>
29+
<arg column="author_id" javaType="domain.blog.Author"
30+
select="org.apache.ibatis.binding.BoundAuthorMapper.selectAuthor"/>
31+
<arg column="id" javaType="java.util.List" select="selectPostsForBlog"/>
32+
</constructor>
33+
</resultMap>
34+
35+
<resultMap id="blogUsingConstructorWithResultMap" type="Blog">
36+
<constructor>
37+
<idArg column="id" javaType="_int"/>
38+
<arg column="title" javaType="java.lang.String"/>
39+
<arg javaType="domain.blog.Author" resultMap="org.apache.ibatis.binding.BoundAuthorMapper.authorResultMap"/>
40+
<arg column="id" javaType="java.util.List" select="selectPostsForBlog"/>
41+
</constructor>
42+
</resultMap>
43+
44+
<select id="selectBlogWithPostsUsingSubSelect" parameterType="int" resultMap="blogWithPosts">
2645
select * from Blog where id = #{id}
2746
</select>
2847

29-
<select id="selectAuthorWithInlineParams"
30-
parameterType="int"
31-
resultType="domain.blog.Author">
48+
<select id="selectAuthorWithInlineParams"
49+
parameterType="int"
50+
resultType="domain.blog.Author">
3251
select * from author where id = #{id}
3352
</select>
3453

35-
<select id="selectPostsForBlog" parameterType="int" resultType="Post">
54+
<select id="selectPostsForBlog" parameterType="int" resultType="Post">
3655
select * from Post where blog_id = #{blog_id}
3756
</select>
3857

58+
<select id="selectBlogByIdUsingConstructor" parameterType="int" resultMap="blogUsingConstructor">
59+
select * from Blog where id = #{id}
60+
</select>
3961

62+
<select id="selectBlogUsingConstructorWithResultMap" parameterType="int"
63+
resultMap="blogUsingConstructorWithResultMap">
64+
select b.*, a.id as author_id, a.username as author_username, a.password as author_password, a.email as author_email, a.bio as author_bio, a.favourite_section from Blog b join Author a on b.author_id = a.id where b.id = #{id}
65+
</select>
4066
</mapper>

0 commit comments

Comments
 (0)