View Javadoc
1   /*
2    *    Copyright 2009-2021 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    *       http://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.binding;
17  
18  import static com.googlecode.catchexception.apis.BDDCatchException.*;
19  import static org.assertj.core.api.BDDAssertions.then;
20  import static org.junit.jupiter.api.Assertions.*;
21  
22  import java.io.IOException;
23  import java.lang.reflect.Method;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.Collections;
27  import java.util.HashMap;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Map;
31  
32  import javassist.util.proxy.Proxy;
33  
34  import javax.sql.DataSource;
35  
36  import net.sf.cglib.proxy.Factory;
37  
38  import org.apache.ibatis.BaseDataTest;
39  import org.apache.ibatis.binding.MapperProxy.MapperMethodInvoker;
40  import org.apache.ibatis.cursor.Cursor;
41  import org.apache.ibatis.domain.blog.Author;
42  import org.apache.ibatis.domain.blog.Blog;
43  import org.apache.ibatis.domain.blog.DraftPost;
44  import org.apache.ibatis.domain.blog.Post;
45  import org.apache.ibatis.domain.blog.Section;
46  import org.apache.ibatis.exceptions.PersistenceException;
47  import org.apache.ibatis.executor.result.DefaultResultHandler;
48  import org.apache.ibatis.mapping.Environment;
49  import org.apache.ibatis.session.Configuration;
50  import org.apache.ibatis.session.RowBounds;
51  import org.apache.ibatis.session.SqlSession;
52  import org.apache.ibatis.session.SqlSessionFactory;
53  import org.apache.ibatis.session.SqlSessionFactoryBuilder;
54  import org.apache.ibatis.transaction.TransactionFactory;
55  import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
56  import org.junit.jupiter.api.Assertions;
57  import org.junit.jupiter.api.BeforeAll;
58  import org.junit.jupiter.api.Disabled;
59  import org.junit.jupiter.api.Test;
60  
61  class BindingTest {
62    private static SqlSessionFactory sqlSessionFactory;
63  
64    @BeforeAll
65    static void setup() throws Exception {
66      DataSource dataSource = BaseDataTest.createBlogDataSource();
67      BaseDataTest.runScript(dataSource, BaseDataTest.BLOG_DDL);
68      BaseDataTest.runScript(dataSource, BaseDataTest.BLOG_DATA);
69      TransactionFactory transactionFactory = new JdbcTransactionFactory();
70      Environment environment = new Environment("Production", transactionFactory, dataSource);
71      Configuration configuration = new Configuration(environment);
72      configuration.setLazyLoadingEnabled(true);
73      configuration.setUseActualParamName(false); // to test legacy style reference (#{0} #{1})
74      configuration.getTypeAliasRegistry().registerAlias(Blog.class);
75      configuration.getTypeAliasRegistry().registerAlias(Post.class);
76      configuration.getTypeAliasRegistry().registerAlias(Author.class);
77      configuration.addMapper(BoundBlogMapper.class);
78      configuration.addMapper(BoundAuthorMapper.class);
79      sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
80    }
81  
82    @Test
83    void shouldSelectBlogWithPostsUsingSubSelect() {
84      try (SqlSession session = sqlSessionFactory.openSession()) {
85        BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
86        Blog b = mapper.selectBlogWithPostsUsingSubSelect(1);
87        assertEquals(1, b.getId());
88        assertNotNull(b.getAuthor());
89        assertEquals(101, b.getAuthor().getId());
90        assertEquals("jim", b.getAuthor().getUsername());
91        assertEquals("********", b.getAuthor().getPassword());
92        assertEquals(2, b.getPosts().size());
93      }
94    }
95  
96    @Test
97    void shouldFindPostsInList() {
98      try (SqlSession session = sqlSessionFactory.openSession()) {
99        BoundAuthorMapper mapper = session.getMapper(BoundAuthorMapper.class);
100       List<Post> posts = mapper.findPostsInList(new ArrayList<Integer>() {{
101         add(1);
102         add(3);
103         add(5);
104       }});
105       assertEquals(3, posts.size());
106       session.rollback();
107     }
108   }
109 
110   @Test
111   void shouldFindPostsInArray() {
112     try (SqlSession session = sqlSessionFactory.openSession()) {
113       BoundAuthorMapper mapper = session.getMapper(BoundAuthorMapper.class);
114       Integer[] params = new Integer[]{1, 3, 5};
115       List<Post> posts = mapper.findPostsInArray(params);
116       assertEquals(3, posts.size());
117       session.rollback();
118     }
119   }
120 
121   @Test
122   void shouldFindThreeSpecificPosts() {
123     try (SqlSession session = sqlSessionFactory.openSession()) {
124       BoundAuthorMapper mapper = session.getMapper(BoundAuthorMapper.class);
125       List<Post> posts = mapper.findThreeSpecificPosts(1, new RowBounds(1, 1), 3, 5);
126       assertEquals(1, posts.size());
127       assertEquals(3, posts.get(0).getId());
128       session.rollback();
129     }
130   }
131 
132   @Test
133   void shouldInsertAuthorWithSelectKey() {
134     try (SqlSession session = sqlSessionFactory.openSession()) {
135       BoundAuthorMapper mapper = session.getMapper(BoundAuthorMapper.class);
136       Author author = new Author(-1, "cbegin", "******", "cbegin@nowhere.com", "N/A", Section.NEWS);
137       int rows = mapper.insertAuthor(author);
138       assertEquals(1, rows);
139       session.rollback();
140     }
141   }
142 
143   @Test
144   void verifyErrorMessageFromSelectKey() {
145     try (SqlSession session = sqlSessionFactory.openSession()) {
146       try {
147         BoundAuthorMapper mapper = session.getMapper(BoundAuthorMapper.class);
148         Author author = new Author(-1, "cbegin", "******", "cbegin@nowhere.com", "N/A", Section.NEWS);
149         when(() -> mapper.insertAuthorInvalidSelectKey(author));
150         then(caughtException()).isInstanceOf(PersistenceException.class).hasMessageContaining(
151             "### The error may exist in org/apache/ibatis/binding/BoundAuthorMapper.xml" + System.lineSeparator() +
152                 "### The error may involve org.apache.ibatis.binding.BoundAuthorMapper.insertAuthorInvalidSelectKey!selectKey" + System.lineSeparator() +
153                 "### The error occurred while executing a query");
154       } finally {
155         session.rollback();
156       }
157     }
158   }
159 
160   @Test
161   void verifyErrorMessageFromInsertAfterSelectKey() {
162     try (SqlSession session = sqlSessionFactory.openSession()) {
163       try {
164         BoundAuthorMapper mapper = session.getMapper(BoundAuthorMapper.class);
165         Author author = new Author(-1, "cbegin", "******", "cbegin@nowhere.com", "N/A", Section.NEWS);
166         when(() -> mapper.insertAuthorInvalidInsert(author));
167         then(caughtException()).isInstanceOf(PersistenceException.class).hasMessageContaining(
168             "### The error may exist in org/apache/ibatis/binding/BoundAuthorMapper.xml" + System.lineSeparator() +
169                 "### The error may involve org.apache.ibatis.binding.BoundAuthorMapper.insertAuthorInvalidInsert" + System.lineSeparator() +
170                 "### The error occurred while executing an update");
171       } finally {
172         session.rollback();
173       }
174     }
175   }
176 
177   @Test
178   void shouldInsertAuthorWithSelectKeyAndDynamicParams() {
179     try (SqlSession session = sqlSessionFactory.openSession()) {
180       BoundAuthorMapper mapper = session.getMapper(BoundAuthorMapper.class);
181       Author author = new Author(-1, "cbegin", "******", "cbegin@nowhere.com", "N/A", Section.NEWS);
182       int rows = mapper.insertAuthorDynamic(author);
183       assertEquals(1, rows);
184       assertNotEquals(-1, author.getId()); // id must be autogenerated
185       Author author2 = mapper.selectAuthor(author.getId());
186       assertNotNull(author2);
187       assertEquals(author.getEmail(), author2.getEmail());
188       session.rollback();
189     }
190   }
191 
192   @Test
193   void shouldSelectRandom() {
194     try (SqlSession session = sqlSessionFactory.openSession()) {
195       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
196       Integer x = mapper.selectRandom();
197       assertNotNull(x);
198     }
199   }
200 
201   @Test
202   void shouldExecuteBoundSelectListOfBlogsStatement() {
203     try (SqlSession session = sqlSessionFactory.openSession()) {
204       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
205       List<Blog> blogs = mapper.selectBlogs();
206       assertEquals(2, blogs.size());
207     }
208   }
209 
210   @Test
211   void shouldExecuteBoundSelectMapOfBlogsById() {
212     try (SqlSession session = sqlSessionFactory.openSession()) {
213       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
214       Map<Integer,Blog> blogs = mapper.selectBlogsAsMapById();
215       assertEquals(2, blogs.size());
216       for(Map.Entry<Integer,Blog> blogEntry : blogs.entrySet()) {
217         assertEquals(blogEntry.getKey(), (Integer) blogEntry.getValue().getId());
218       }
219     }
220   }
221 
222   @Test
223   void shouldExecuteMultipleBoundSelectOfBlogsByIdInWithProvidedResultHandlerBetweenSessions() {
224     final DefaultResultHandler handler = new DefaultResultHandler();
225     try (SqlSession session = sqlSessionFactory.openSession()) {
226       session.select("selectBlogsAsMapById", handler);
227     }
228 
229     final DefaultResultHandler moreHandler = new DefaultResultHandler();
230     try (SqlSession session = sqlSessionFactory.openSession()) {
231       session.select("selectBlogsAsMapById", moreHandler);
232     }
233     assertEquals(2, handler.getResultList().size());
234     assertEquals(2, moreHandler.getResultList().size());
235   }
236 
237   @Test
238   void shouldExecuteMultipleBoundSelectOfBlogsByIdInWithProvidedResultHandlerInSameSession() {
239     try (SqlSession session = sqlSessionFactory.openSession()) {
240       final DefaultResultHandler handler = new DefaultResultHandler();
241       session.select("selectBlogsAsMapById", handler);
242 
243       final DefaultResultHandler moreHandler = new DefaultResultHandler();
244       session.select("selectBlogsAsMapById", moreHandler);
245 
246       assertEquals(2, handler.getResultList().size());
247       assertEquals(2, moreHandler.getResultList().size());
248     }
249   }
250 
251   @Test
252   void shouldExecuteMultipleBoundSelectMapOfBlogsByIdInSameSessionWithoutClearingLocalCache() {
253     try (SqlSession session = sqlSessionFactory.openSession()) {
254       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
255       Map<Integer,Blog> blogs = mapper.selectBlogsAsMapById();
256       Map<Integer,Blog> moreBlogs = mapper.selectBlogsAsMapById();
257       assertEquals(2, blogs.size());
258       assertEquals(2, moreBlogs.size());
259       for(Map.Entry<Integer,Blog> blogEntry : blogs.entrySet()) {
260         assertEquals(blogEntry.getKey(), (Integer) blogEntry.getValue().getId());
261       }
262       for(Map.Entry<Integer,Blog> blogEntry : moreBlogs.entrySet()) {
263         assertEquals(blogEntry.getKey(), (Integer) blogEntry.getValue().getId());
264       }
265     }
266   }
267 
268   @Test
269   void shouldExecuteMultipleBoundSelectMapOfBlogsByIdBetweenTwoSessionsWithGlobalCacheEnabled() {
270     Map<Integer,Blog> blogs;
271     try (SqlSession session = sqlSessionFactory.openSession()) {
272       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
273       blogs = mapper.selectBlogsAsMapById();
274     }
275     try (SqlSession session = sqlSessionFactory.openSession()) {
276       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
277       Map<Integer,Blog> moreBlogs = mapper.selectBlogsAsMapById();
278       assertEquals(2, blogs.size());
279       assertEquals(2, moreBlogs.size());
280       for(Map.Entry<Integer,Blog> blogEntry : blogs.entrySet()) {
281         assertEquals(blogEntry.getKey(), (Integer) blogEntry.getValue().getId());
282       }
283       for(Map.Entry<Integer,Blog> blogEntry : moreBlogs.entrySet()) {
284         assertEquals(blogEntry.getKey(), (Integer) blogEntry.getValue().getId());
285       }
286     }
287   }
288 
289   @Test
290   void shouldSelectListOfBlogsUsingXMLConfig() {
291     try (SqlSession session = sqlSessionFactory.openSession()) {
292       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
293       List<Blog> blogs = mapper.selectBlogsFromXML();
294       assertEquals(2, blogs.size());
295     }
296   }
297 
298   @Test
299   void shouldExecuteBoundSelectListOfBlogsStatementUsingProvider() {
300     try (SqlSession session = sqlSessionFactory.openSession()) {
301       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
302       List<Blog> blogs = mapper.selectBlogsUsingProvider();
303       assertEquals(2, blogs.size());
304     }
305   }
306 
307   @Test
308   void shouldExecuteBoundSelectListOfBlogsAsMaps() {
309     try (SqlSession session = sqlSessionFactory.openSession()) {
310       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
311       List<Map<String,Object>> blogs = mapper.selectBlogsAsMaps();
312       assertEquals(2, blogs.size());
313     }
314   }
315 
316   @Test
317   void shouldSelectListOfPostsLike() {
318     try (SqlSession session = sqlSessionFactory.openSession()) {
319       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
320       List<Post> posts = mapper.selectPostsLike(new RowBounds(1,1),"%a%");
321       assertEquals(1, posts.size());
322     }
323   }
324 
325   @Test
326   void shouldSelectListOfPostsLikeTwoParameters() {
327     try (SqlSession session = sqlSessionFactory.openSession()) {
328       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
329       List<Post> posts = mapper.selectPostsLikeSubjectAndBody(new RowBounds(1,1),"%a%","%a%");
330       assertEquals(1, posts.size());
331     }
332   }
333 
334   @Test
335   void shouldExecuteBoundSelectOneBlogStatement() {
336     try (SqlSession session = sqlSessionFactory.openSession()) {
337       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
338       Blog blog = mapper.selectBlog(1);
339       assertEquals(1, blog.getId());
340       assertEquals("Jim Business", blog.getTitle());
341     }
342   }
343 
344   @Test
345   void shouldExecuteBoundSelectOneBlogStatementWithConstructor() {
346     try (SqlSession session = sqlSessionFactory.openSession()) {
347       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
348       Blog blog = mapper.selectBlogUsingConstructor(1);
349       assertEquals(1, blog.getId());
350       assertEquals("Jim Business", blog.getTitle());
351       assertNotNull(blog.getAuthor(), "author should not be null");
352       List<Post> posts = blog.getPosts();
353       assertTrue(posts != null && !posts.isEmpty(), "posts should not be empty");
354     }
355   }
356 
357   @Test
358   void shouldExecuteBoundSelectBlogUsingConstructorWithResultMap() {
359     try (SqlSession session = sqlSessionFactory.openSession()) {
360       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
361       Blog blog = mapper.selectBlogUsingConstructorWithResultMap(1);
362       assertEquals(1, blog.getId());
363       assertEquals("Jim Business", blog.getTitle());
364       assertNotNull(blog.getAuthor(), "author should not be null");
365       List<Post> posts = blog.getPosts();
366       assertTrue(posts != null && !posts.isEmpty(), "posts should not be empty");
367     }
368   }
369 
370   @Test
371   void shouldExecuteBoundSelectBlogUsingConstructorWithResultMapAndProperties() {
372     try (SqlSession session = sqlSessionFactory.openSession()) {
373       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
374       Blog blog = mapper.selectBlogUsingConstructorWithResultMapAndProperties(1);
375       assertEquals(1, blog.getId());
376       assertEquals("Jim Business", blog.getTitle());
377       assertNotNull(blog.getAuthor(), "author should not be null");
378       Author author = blog.getAuthor();
379       assertEquals(101, author.getId());
380       assertEquals("jim@ibatis.apache.org", author.getEmail());
381       assertEquals("jim", author.getUsername());
382       assertEquals(Section.NEWS, author.getFavouriteSection());
383       List<Post> posts = blog.getPosts();
384       assertNotNull(posts, "posts should not be empty");
385       assertEquals(2, posts.size());
386     }
387   }
388 
389   @Disabled
390   @Test // issue #480 and #101
391   void shouldExecuteBoundSelectBlogUsingConstructorWithResultMapCollection() {
392     try (SqlSession session = sqlSessionFactory.openSession()) {
393       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
394       Blog blog = mapper.selectBlogUsingConstructorWithResultMapCollection(1);
395       assertEquals(1, blog.getId());
396       assertEquals("Jim Business", blog.getTitle());
397       assertNotNull(blog.getAuthor(), "author should not be null");
398       List<Post> posts = blog.getPosts();
399       assertTrue(posts != null && !posts.isEmpty(), "posts should not be empty");
400     }
401   }
402 
403   @Test
404   void shouldExecuteBoundSelectOneBlogStatementWithConstructorUsingXMLConfig() {
405     try (SqlSession session = sqlSessionFactory.openSession()) {
406       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
407       Blog blog = mapper.selectBlogByIdUsingConstructor(1);
408       assertEquals(1, blog.getId());
409       assertEquals("Jim Business", blog.getTitle());
410       assertNotNull(blog.getAuthor(), "author should not be null");
411       List<Post> posts = blog.getPosts();
412       assertTrue(posts != null && !posts.isEmpty(), "posts should not be empty");
413     }
414   }
415 
416   @Test
417   void shouldSelectOneBlogAsMap() {
418     try (SqlSession session = sqlSessionFactory.openSession()) {
419       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
420       Map<String,Object> blog = mapper.selectBlogAsMap(new HashMap<String, Object>() {
421         {
422           put("id", 1);
423         }
424       });
425       assertEquals(1, blog.get("ID"));
426       assertEquals("Jim Business", blog.get("TITLE"));
427     }
428   }
429 
430   @Test
431   void shouldSelectOneAuthor() {
432     try (SqlSession session = sqlSessionFactory.openSession()) {
433       BoundAuthorMapper mapper = session.getMapper(BoundAuthorMapper.class);
434       Author author = mapper.selectAuthor(101);
435       assertEquals(101, author.getId());
436       assertEquals("jim", author.getUsername());
437       assertEquals("********", author.getPassword());
438       assertEquals("jim@ibatis.apache.org", author.getEmail());
439       assertEquals("", author.getBio());
440     }
441   }
442 
443   @Test
444   void shouldSelectOneAuthorFromCache() {
445     Author author1 = selectOneAuthor();
446     Author author2 = selectOneAuthor();
447     assertSame(author1, author2, "Same (cached) instance should be returned unless rollback is called.");
448   }
449 
450   private Author selectOneAuthor() {
451     try (SqlSession session = sqlSessionFactory.openSession()) {
452       BoundAuthorMapper mapper = session.getMapper(BoundAuthorMapper.class);
453       return mapper.selectAuthor(101);
454     }
455   }
456 
457   @Test
458   void shouldSelectOneAuthorByConstructor() {
459     try (SqlSession session = sqlSessionFactory.openSession()) {
460       BoundAuthorMapper mapper = session.getMapper(BoundAuthorMapper.class);
461       Author author = mapper.selectAuthorConstructor(101);
462       assertEquals(101, author.getId());
463       assertEquals("jim", author.getUsername());
464       assertEquals("********", author.getPassword());
465       assertEquals("jim@ibatis.apache.org", author.getEmail());
466       assertEquals("", author.getBio());
467     }
468   }
469 
470   @Test
471   void shouldSelectDraftTypedPosts() {
472     try (SqlSession session = sqlSessionFactory.openSession()) {
473       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
474       List<Post> posts = mapper.selectPosts();
475       assertEquals(5, posts.size());
476       assertTrue(posts.get(0) instanceof DraftPost);
477       assertFalse(posts.get(1) instanceof DraftPost);
478       assertTrue(posts.get(2) instanceof DraftPost);
479       assertFalse(posts.get(3) instanceof DraftPost);
480       assertFalse(posts.get(4) instanceof DraftPost);
481     }
482   }
483 
484   @Test
485   void shouldSelectDraftTypedPostsWithResultMap() {
486     try (SqlSession session = sqlSessionFactory.openSession()) {
487       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
488       List<Post> posts = mapper.selectPostsWithResultMap();
489       assertEquals(5, posts.size());
490       assertTrue(posts.get(0) instanceof DraftPost);
491       assertFalse(posts.get(1) instanceof DraftPost);
492       assertTrue(posts.get(2) instanceof DraftPost);
493       assertFalse(posts.get(3) instanceof DraftPost);
494       assertFalse(posts.get(4) instanceof DraftPost);
495     }
496   }
497 
498   @Test
499   void shouldReturnANotNullToString() {
500     try (SqlSession session = sqlSessionFactory.openSession()) {
501       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
502       assertNotNull(mapper.toString());
503     }
504   }
505 
506   @Test
507   void shouldReturnANotNullHashCode() {
508     try (SqlSession session = sqlSessionFactory.openSession()) {
509       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
510       assertNotNull(mapper.hashCode());
511     }
512   }
513 
514   @Test
515   void shouldCompareTwoMappers() {
516     try (SqlSession session = sqlSessionFactory.openSession()) {
517       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
518       BoundBlogMapper mapper2 = session.getMapper(BoundBlogMapper.class);
519       assertNotEquals(mapper, mapper2);
520     }
521   }
522 
523   @Test
524   void shouldFailWhenSelectingOneBlogWithNonExistentParam() {
525     try (SqlSession session = sqlSessionFactory.openSession()) {
526       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
527       assertThrows(Exception.class, () -> mapper.selectBlogByNonExistentParam(1));
528     }
529   }
530 
531   @Test
532   void shouldFailWhenSelectingOneBlogWithNullParam() {
533     try (SqlSession session = sqlSessionFactory.openSession()) {
534       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
535       assertThrows(Exception.class, () -> mapper.selectBlogByNullParam(null));
536     }
537   }
538 
539   @Test // Decided that maps are dynamic so no existent params do not fail
540   void shouldFailWhenSelectingOneBlogWithNonExistentNestedParam() {
541     try (SqlSession session = sqlSessionFactory.openSession()) {
542       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
543       mapper.selectBlogByNonExistentNestedParam(1, Collections.<String, Object>emptyMap());
544     }
545   }
546 
547   @Test
548   void shouldSelectBlogWithDefault30ParamNames() {
549     try (SqlSession session = sqlSessionFactory.openSession()) {
550       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
551       Blog blog = mapper.selectBlogByDefault30ParamNames(1, "Jim Business");
552       assertNotNull(blog);
553     }
554   }
555 
556   @Test
557   void shouldSelectBlogWithDefault31ParamNames() {
558     try (SqlSession session = sqlSessionFactory.openSession()) {
559       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
560       Blog blog = mapper.selectBlogByDefault31ParamNames(1, "Jim Business");
561       assertNotNull(blog);
562     }
563   }
564 
565   @Test
566   void shouldSelectBlogWithAParamNamedValue() {
567     try (SqlSession session = sqlSessionFactory.openSession()) {
568       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
569       Blog blog = mapper.selectBlogWithAParamNamedValue("id", 1, "Jim Business");
570       assertNotNull(blog);
571     }
572   }
573 
574   @Test
575   void shouldCacheMapperMethod() throws Exception {
576     try (SqlSession session = sqlSessionFactory.openSession()) {
577 
578       // Create another mapper instance with a method cache we can test against:
579       final MapperProxyFactory<BoundBlogMapper> mapperProxyFactory = new MapperProxyFactory<BoundBlogMapper>(BoundBlogMapper.class);
580       assertEquals(BoundBlogMapper.class, mapperProxyFactory.getMapperInterface());
581       final BoundBlogMapper mapper = mapperProxyFactory.newInstance(session);
582       assertNotSame(mapper, mapperProxyFactory.newInstance(session));
583       assertTrue(mapperProxyFactory.getMethodCache().isEmpty());
584 
585       // Mapper methods we will call later:
586       final Method selectBlog = BoundBlogMapper.class.getMethod("selectBlog", Integer.TYPE);
587       final Method selectBlogByIdUsingConstructor = BoundBlogMapper.class.getMethod("selectBlogByIdUsingConstructor", Integer.TYPE);
588 
589       // Call mapper method and verify it is cached:
590       mapper.selectBlog(1);
591       assertEquals(1, mapperProxyFactory.getMethodCache().size());
592       assertTrue(mapperProxyFactory.getMethodCache().containsKey(selectBlog));
593       final MapperMethodInvoker cachedSelectBlog = mapperProxyFactory.getMethodCache().get(selectBlog);
594 
595       // Call mapper method again and verify the cache is unchanged:
596       session.clearCache();
597       mapper.selectBlog(1);
598       assertEquals(1, mapperProxyFactory.getMethodCache().size());
599       assertSame(cachedSelectBlog, mapperProxyFactory.getMethodCache().get(selectBlog));
600 
601       // Call another mapper method and verify that it shows up in the cache as well:
602       session.clearCache();
603       mapper.selectBlogByIdUsingConstructor(1);
604       assertEquals(2, mapperProxyFactory.getMethodCache().size());
605       assertSame(cachedSelectBlog, mapperProxyFactory.getMethodCache().get(selectBlog));
606       assertTrue(mapperProxyFactory.getMethodCache().containsKey(selectBlogByIdUsingConstructor));
607     }
608   }
609 
610   @Test
611   void shouldGetBlogsWithAuthorsAndPosts() {
612     try (SqlSession session = sqlSessionFactory.openSession()) {
613       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
614       List<Blog> blogs = mapper.selectBlogsWithAutorAndPosts();
615       assertEquals(2, blogs.size());
616       assertTrue(blogs.get(0) instanceof Proxy);
617       assertEquals(101, blogs.get(0).getAuthor().getId());
618       assertEquals(1, blogs.get(0).getPosts().size());
619       assertEquals(1, blogs.get(0).getPosts().get(0).getId());
620       assertTrue(blogs.get(1) instanceof Proxy);
621       assertEquals(102, blogs.get(1).getAuthor().getId());
622       assertEquals(1, blogs.get(1).getPosts().size());
623       assertEquals(2, blogs.get(1).getPosts().get(0).getId());
624     }
625   }
626 
627   @Test
628   void shouldGetBlogsWithAuthorsAndPostsEagerly() {
629     try (SqlSession session = sqlSessionFactory.openSession()) {
630       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
631       List<Blog> blogs = mapper.selectBlogsWithAutorAndPostsEagerly();
632       assertEquals(2, blogs.size());
633       assertFalse(blogs.get(0) instanceof Factory);
634       assertEquals(101, blogs.get(0).getAuthor().getId());
635       assertEquals(1, blogs.get(0).getPosts().size());
636       assertEquals(1, blogs.get(0).getPosts().get(0).getId());
637       assertFalse(blogs.get(1) instanceof Factory);
638       assertEquals(102, blogs.get(1).getAuthor().getId());
639       assertEquals(1, blogs.get(1).getPosts().size());
640       assertEquals(2, blogs.get(1).getPosts().get(0).getId());
641     }
642   }
643 
644   @Test
645   void executeWithResultHandlerAndRowBounds() {
646     try (SqlSession session = sqlSessionFactory.openSession()) {
647       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
648       final DefaultResultHandler handler = new DefaultResultHandler();
649       mapper.collectRangeBlogs(handler, new RowBounds(1, 1));
650 
651       assertEquals(1, handler.getResultList().size());
652       Blog blog = (Blog) handler.getResultList().get(0);
653       assertEquals(2, blog.getId());
654     }
655   }
656 
657   @Test
658   void executeWithMapKeyAndRowBounds() {
659     try (SqlSession session = sqlSessionFactory.openSession()) {
660       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
661       Map<Integer, Blog> blogs = mapper.selectRangeBlogsAsMapById(new RowBounds(1, 1));
662 
663       assertEquals(1, blogs.size());
664       Blog blog = blogs.get(2);
665       assertEquals(2, blog.getId());
666     }
667   }
668 
669   @Test
670   void executeWithCursorAndRowBounds() {
671     try (SqlSession session = sqlSessionFactory.openSession()) {
672       BoundBlogMapper mapper = session.getMapper(BoundBlogMapper.class);
673       try (Cursor<Blog> blogs = mapper.openRangeBlogs(new RowBounds(1, 1)) ) {
674         Iterator<Blog> blogIterator = blogs.iterator();
675         Blog blog = blogIterator.next();
676         assertEquals(2, blog.getId());
677         assertFalse(blogIterator.hasNext());
678       }
679     } catch (IOException e) {
680       Assertions.fail(e.getMessage());
681     }
682   }
683 
684   @Test
685   void registeredMappers() {
686     Collection<Class<?>> mapperClasses = sqlSessionFactory.getConfiguration().getMapperRegistry().getMappers();
687     assertEquals(2, mapperClasses.size());
688     assertTrue(mapperClasses.contains(BoundBlogMapper.class));
689     assertTrue(mapperClasses.contains(BoundAuthorMapper.class));
690   }
691 
692   @Test
693   void shouldMapPropertiesUsingRepeatableAnnotation() {
694     try (SqlSession session = sqlSessionFactory.openSession()) {
695       BoundAuthorMapper mapper = session.getMapper(BoundAuthorMapper.class);
696       Author author = new Author(-1, "cbegin", "******", "cbegin@nowhere.com", "N/A", Section.NEWS);
697       mapper.insertAuthor(author);
698       Author author2 = mapper.selectAuthorMapToPropertiesUsingRepeatable(author.getId());
699       assertNotNull(author2);
700       assertEquals(author.getId(), author2.getId());
701       assertEquals(author.getUsername(), author2.getUsername());
702       assertEquals(author.getPassword(), author2.getPassword());
703       assertEquals(author.getBio(), author2.getBio());
704       assertEquals(author.getEmail(), author2.getEmail());
705       session.rollback();
706     }
707   }
708 
709   @Test
710   void shouldMapConstructorUsingRepeatableAnnotation() {
711     try (SqlSession session = sqlSessionFactory.openSession()) {
712       BoundAuthorMapper mapper = session.getMapper(BoundAuthorMapper.class);
713       Author author = new Author(-1, "cbegin", "******", "cbegin@nowhere.com", "N/A", Section.NEWS);
714       mapper.insertAuthor(author);
715       Author author2 = mapper.selectAuthorMapToConstructorUsingRepeatable(author.getId());
716       assertNotNull(author2);
717       assertEquals(author.getId(), author2.getId());
718       assertEquals(author.getUsername(), author2.getUsername());
719       assertEquals(author.getPassword(), author2.getPassword());
720       assertEquals(author.getBio(), author2.getBio());
721       assertEquals(author.getEmail(), author2.getEmail());
722       assertEquals(author.getFavouriteSection(), author2.getFavouriteSection());
723       session.rollback();
724     }
725   }
726 
727   @Test
728   void shouldMapUsingSingleRepeatableAnnotation() {
729     try (SqlSession session = sqlSessionFactory.openSession()) {
730       BoundAuthorMapper mapper = session.getMapper(BoundAuthorMapper.class);
731       Author author = new Author(-1, "cbegin", "******", "cbegin@nowhere.com", "N/A", Section.NEWS);
732       mapper.insertAuthor(author);
733       Author author2 = mapper.selectAuthorUsingSingleRepeatable(author.getId());
734       assertNotNull(author2);
735       assertEquals(author.getId(), author2.getId());
736       assertEquals(author.getUsername(), author2.getUsername());
737       assertNull(author2.getPassword());
738       assertNull(author2.getBio());
739       assertNull(author2.getEmail());
740       assertNull(author2.getFavouriteSection());
741       session.rollback();
742     }
743   }
744 
745   @Test
746   void shouldMapWhenSpecifyBothArgAndConstructorArgs() {
747     try (SqlSession session = sqlSessionFactory.openSession()) {
748       BoundAuthorMapper mapper = session.getMapper(BoundAuthorMapper.class);
749       Author author = new Author(-1, "cbegin", "******", "cbegin@nowhere.com", "N/A", Section.NEWS);
750       mapper.insertAuthor(author);
751       Author author2 = mapper.selectAuthorUsingBothArgAndConstructorArgs(author.getId());
752       assertNotNull(author2);
753       assertEquals(author.getId(), author2.getId());
754       assertEquals(author.getUsername(), author2.getUsername());
755       assertEquals(author.getPassword(), author2.getPassword());
756       assertEquals(author.getBio(), author2.getBio());
757       assertEquals(author.getEmail(), author2.getEmail());
758       assertEquals(author.getFavouriteSection(), author2.getFavouriteSection());
759       session.rollback();
760     }
761   }
762 
763   @Test
764   void shouldMapWhenSpecifyBothResultAndResults() {
765     try (SqlSession session = sqlSessionFactory.openSession()) {
766       BoundAuthorMapper mapper = session.getMapper(BoundAuthorMapper.class);
767       Author author = new Author(-1, "cbegin", "******", "cbegin@nowhere.com", "N/A", Section.NEWS);
768       mapper.insertAuthor(author);
769       Author author2 = mapper.selectAuthorUsingBothResultAndResults(author.getId());
770       assertNotNull(author2);
771       assertEquals(author.getId(), author2.getId());
772       assertEquals(author.getUsername(), author2.getUsername());
773       assertNull(author2.getPassword());
774       assertNull(author2.getBio());
775       assertNull(author2.getEmail());
776       assertNull(author2.getFavouriteSection());
777       session.rollback();
778     }
779   }
780 
781 }
782