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.session;
17  
18  import static org.junit.jupiter.api.Assertions.assertEquals;
19  import static org.junit.jupiter.api.Assertions.assertNotNull;
20  import static org.junit.jupiter.api.Assertions.assertNull;
21  import static org.junit.jupiter.api.Assertions.assertTrue;
22  import static org.junit.jupiter.api.Assertions.fail;
23  
24  import java.io.Reader;
25  import java.util.ArrayList;
26  import java.util.Collection;
27  import java.util.HashMap;
28  import java.util.LinkedHashMap;
29  import java.util.List;
30  import java.util.Map;
31  
32  import javassist.util.proxy.Proxy;
33  
34  import org.apache.ibatis.BaseDataTest;
35  import org.apache.ibatis.binding.BindingException;
36  import org.apache.ibatis.cache.impl.PerpetualCache;
37  import org.apache.ibatis.domain.blog.Author;
38  import org.apache.ibatis.domain.blog.Blog;
39  import org.apache.ibatis.domain.blog.Comment;
40  import org.apache.ibatis.domain.blog.DraftPost;
41  import org.apache.ibatis.domain.blog.ImmutableAuthor;
42  import org.apache.ibatis.domain.blog.Post;
43  import org.apache.ibatis.domain.blog.Section;
44  import org.apache.ibatis.domain.blog.Tag;
45  import org.apache.ibatis.domain.blog.mappers.AuthorMapper;
46  import org.apache.ibatis.domain.blog.mappers.AuthorMapperWithMultipleHandlers;
47  import org.apache.ibatis.domain.blog.mappers.AuthorMapperWithRowBounds;
48  import org.apache.ibatis.domain.blog.mappers.BlogMapper;
49  import org.apache.ibatis.exceptions.TooManyResultsException;
50  import org.apache.ibatis.executor.result.DefaultResultHandler;
51  import org.apache.ibatis.io.Resources;
52  import org.apache.ibatis.mapping.MappedStatement;
53  import org.apache.ibatis.mapping.SqlCommandType;
54  import org.apache.ibatis.mapping.SqlSource;
55  import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;
56  import org.junit.jupiter.api.Assertions;
57  import org.junit.jupiter.api.BeforeAll;
58  import org.junit.jupiter.api.Test;
59  import org.mockito.Mockito;
60  
61  class SqlSessionTest extends BaseDataTest {
62    private static SqlSessionFactory sqlMapper;
63  
64    @BeforeAll
65    static void setup() throws Exception {
66      createBlogDataSource();
67      final String resource = "org/apache/ibatis/builder/MapperConfig.xml";
68      final Reader reader = Resources.getResourceAsReader(resource);
69      sqlMapper = new SqlSessionFactoryBuilder().build(reader);
70    }
71  
72    @Test
73    void shouldResolveBothSimpleNameAndFullyQualifiedName() {
74      Configuration c = new Configuration();
75      final String fullName = "com.mycache.MyCache";
76      final String shortName = "MyCache";
77      final PerpetualCache cache = new PerpetualCache(fullName);
78      c.addCache(cache);
79      assertEquals(cache, c.getCache(fullName));
80      assertEquals(cache, c.getCache(shortName));
81    }
82  
83    @Test
84    void shouldFailOverToMostApplicableSimpleName() {
85      Configuration c = new Configuration();
86      final String fullName = "com.mycache.MyCache";
87      final String invalidName = "unknown.namespace.MyCache";
88      final PerpetualCache cache = new PerpetualCache(fullName);
89      c.addCache(cache);
90      assertEquals(cache, c.getCache(fullName));
91      Assertions.assertThrows(IllegalArgumentException.class, () -> c.getCache(invalidName));
92    }
93  
94    @Test
95    void shouldSucceedWhenFullyQualifiedButFailDueToAmbiguity() {
96      Configuration c = new Configuration();
97  
98      final String name1 = "com.mycache.MyCache";
99      final PerpetualCache cache1 = new PerpetualCache(name1);
100     c.addCache(cache1);
101 
102     final String name2 = "com.other.MyCache";
103     final PerpetualCache cache2 = new PerpetualCache(name2);
104     c.addCache(cache2);
105 
106     final String shortName = "MyCache";
107 
108     assertEquals(cache1, c.getCache(name1));
109     assertEquals(cache2, c.getCache(name2));
110 
111     try {
112       c.getCache(shortName);
113       fail("Exception expected.");
114     } catch (Exception e) {
115       assertTrue(e.getMessage().contains("ambiguous"));
116     }
117 
118   }
119 
120   @Test
121   void shouldFailToAddDueToNameConflict() {
122     Configuration c = new Configuration();
123     final String fullName = "com.mycache.MyCache";
124     final PerpetualCache cache = new PerpetualCache(fullName);
125     try {
126       c.addCache(cache);
127       c.addCache(cache);
128       fail("Exception expected.");
129     } catch (Exception e) {
130       assertTrue(e.getMessage().contains("already contains value"));
131     }
132   }
133 
134   @Test
135   void shouldOpenAndClose() {
136     SqlSession session = sqlMapper.openSession(TransactionIsolationLevel.SERIALIZABLE);
137     session.close();
138   }
139 
140   @Test
141   void shouldCommitAnUnUsedSqlSession() {
142     try (SqlSession session = sqlMapper.openSession(TransactionIsolationLevel.SERIALIZABLE)) {
143       session.commit(true);
144     }
145   }
146 
147   @Test
148   void shouldRollbackAnUnUsedSqlSession() {
149     try (SqlSession session = sqlMapper.openSession(TransactionIsolationLevel.SERIALIZABLE)) {
150       session.rollback(true);
151     }
152   }
153 
154   @Test
155   void shouldSelectAllAuthors() {
156     try (SqlSession session = sqlMapper.openSession(TransactionIsolationLevel.SERIALIZABLE)) {
157       List<Author> authors = session.selectList("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAllAuthors");
158       assertEquals(2, authors.size());
159     }
160   }
161 
162   @Test
163   void shouldFailWithTooManyResultsException() {
164     try (SqlSession session = sqlMapper.openSession(TransactionIsolationLevel.SERIALIZABLE)) {
165       Assertions.assertThrows(TooManyResultsException.class, () -> {
166         session.selectOne("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAllAuthors");
167       });
168     }
169   }
170 
171   @Test
172   void shouldSelectAllAuthorsAsMap() {
173     try (SqlSession session = sqlMapper.openSession(TransactionIsolationLevel.SERIALIZABLE)) {
174       final Map<Integer,Author> authors = session.selectMap("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAllAuthors", "id");
175       assertEquals(2, authors.size());
176       for(Map.Entry<Integer,Author> authorEntry : authors.entrySet()) {
177         assertEquals(authorEntry.getKey(), (Integer) authorEntry.getValue().getId());
178       }
179     }
180   }
181 
182   @Test
183   void shouldSelectCountOfPosts() {
184     try (SqlSession session = sqlMapper.openSession()) {
185       Integer count = session.selectOne("org.apache.ibatis.domain.blog.mappers.BlogMapper.selectCountOfPosts");
186       assertEquals(5, count.intValue());
187     }
188   }
189 
190   @Test
191   void shouldEnsureThatBothEarlyAndLateResolutionOfNesteDiscriminatorsResolesToUseNestedResultSetHandler() {
192       Configuration configuration = sqlMapper.getConfiguration();
193       assertTrue(configuration.getResultMap("org.apache.ibatis.domain.blog.mappers.BlogMapper.earlyNestedDiscriminatorPost").hasNestedResultMaps());
194       assertTrue(configuration.getResultMap("org.apache.ibatis.domain.blog.mappers.BlogMapper.lateNestedDiscriminatorPost").hasNestedResultMaps());
195   }
196 
197   @Test
198   void shouldSelectOneAuthor() {
199     try (SqlSession session = sqlMapper.openSession()) {
200       Author author = session.selectOne(
201           "org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor", new Author(101));
202       assertEquals(101, author.getId());
203       assertEquals(Section.NEWS, author.getFavouriteSection());
204     }
205   }
206 
207   @Test
208   void shouldSelectOneAuthorAsList() {
209     try (SqlSession session = sqlMapper.openSession()) {
210       List<Author> authors = session.selectList(
211           "org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor", new Author(101));
212       assertEquals(101, authors.get(0).getId());
213       assertEquals(Section.NEWS, authors.get(0).getFavouriteSection());
214     }
215   }
216 
217   @Test
218   void shouldSelectOneImmutableAuthor() {
219     try (SqlSession session = sqlMapper.openSession()) {
220       ImmutableAuthor author = session.selectOne(
221           "org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectImmutableAuthor", new Author(101));
222       assertEquals(101, author.getId());
223       assertEquals(Section.NEWS, author.getFavouriteSection());
224     }
225   }
226 
227   @Test
228   void shouldSelectOneAuthorWithInlineParams() {
229     try (SqlSession session = sqlMapper.openSession()) {
230       Author author = session.selectOne(
231           "org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthorWithInlineParams", new Author(101));
232       assertEquals(101, author.getId());
233     }
234   }
235 
236   @Test
237   void shouldInsertAuthor() {
238     try (SqlSession session = sqlMapper.openSession()) {
239       Author expected = new Author(500, "cbegin", "******", "cbegin@somewhere.com", "Something...", null);
240       int updates = session.insert("org.apache.ibatis.domain.blog.mappers.AuthorMapper.insertAuthor", expected);
241       assertEquals(1, updates);
242       Author actual = session.selectOne("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor", new Author(500));
243       assertNotNull(actual);
244       assertEquals(expected.getId(), actual.getId());
245       assertEquals(expected.getUsername(), actual.getUsername());
246       assertEquals(expected.getPassword(), actual.getPassword());
247       assertEquals(expected.getEmail(), actual.getEmail());
248       assertEquals(expected.getBio(), actual.getBio());
249     }
250   }
251 
252   @Test
253   void shouldUpdateAuthorImplicitRollback() {
254     try (SqlSession session = sqlMapper.openSession()) {
255       Author original = session.selectOne("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor", 101);
256       original.setEmail("new@email.com");
257       int updates = session.update("org.apache.ibatis.domain.blog.mappers.AuthorMapper.updateAuthor", original);
258       assertEquals(1, updates);
259       Author updated = session.selectOne("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor", 101);
260       assertEquals(original.getEmail(), updated.getEmail());
261     }
262     try (SqlSession session = sqlMapper.openSession()) {
263       Author updated = session.selectOne("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor", 101);
264       assertEquals("jim@ibatis.apache.org", updated.getEmail());
265     }
266   }
267 
268   @Test
269   void shouldUpdateAuthorCommit() {
270     Author original;
271     try (SqlSession session = sqlMapper.openSession()) {
272       original = session.selectOne("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor", 101);
273       original.setEmail("new@email.com");
274       int updates = session.update("org.apache.ibatis.domain.blog.mappers.AuthorMapper.updateAuthor", original);
275       assertEquals(1, updates);
276       Author updated = session.selectOne("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor", 101);
277       assertEquals(original.getEmail(), updated.getEmail());
278       session.commit();
279     }
280     try (SqlSession session = sqlMapper.openSession()) {
281       Author updated = session.selectOne("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor", 101);
282       assertEquals(original.getEmail(), updated.getEmail());
283     }
284   }
285 
286   @Test
287   void shouldUpdateAuthorIfNecessary() {
288     Author original;
289     try (SqlSession session = sqlMapper.openSession()) {
290       original = session.selectOne("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor", 101);
291       original.setEmail("new@email.com");
292       original.setBio(null);
293       int updates = session.update("org.apache.ibatis.domain.blog.mappers.AuthorMapper.updateAuthorIfNecessary", original);
294       assertEquals(1, updates);
295       Author updated = session.selectOne("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor", 101);
296       assertEquals(original.getEmail(), updated.getEmail());
297       session.commit();
298     }
299     try (SqlSession session = sqlMapper.openSession()) {
300       Author updated = session.selectOne("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor", 101);
301       assertEquals(original.getEmail(), updated.getEmail());
302     }
303   }
304 
305   @Test
306   void shouldDeleteAuthor() {
307     try (SqlSession session = sqlMapper.openSession()) {
308       final int id = 102;
309 
310       List<Author> authors = session.selectList("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor", id);
311       assertEquals(1, authors.size());
312 
313       int updates = session.delete("org.apache.ibatis.domain.blog.mappers.AuthorMapper.deleteAuthor", id);
314       assertEquals(1, updates);
315 
316       authors = session.selectList("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor", id);
317       assertEquals(0, authors.size());
318 
319       session.rollback();
320       authors = session.selectList("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor", id);
321       assertEquals(1, authors.size());
322     }
323   }
324 
325   @Test
326   void shouldSelectBlogWithPostsAndAuthorUsingSubSelects() {
327     try (SqlSession session = sqlMapper.openSession()) {
328       Blog blog = session.selectOne("org.apache.ibatis.domain.blog.mappers.BlogMapper.selectBlogWithPostsUsingSubSelect", 1);
329       assertEquals("Jim Business", blog.getTitle());
330       assertEquals(2, blog.getPosts().size());
331       assertEquals("Corn nuts", blog.getPosts().get(0).getSubject());
332       assertEquals(101, blog.getAuthor().getId());
333       assertEquals("jim", blog.getAuthor().getUsername());
334     }
335   }
336 
337   @Test
338   void shouldSelectBlogWithPostsAndAuthorUsingSubSelectsLazily() {
339     try (SqlSession session = sqlMapper.openSession()) {
340       Blog blog = session.selectOne("org.apache.ibatis.domain.blog.mappers.BlogMapper.selectBlogWithPostsUsingSubSelectLazily", 1);
341       Assertions.assertTrue(blog instanceof Proxy);
342       assertEquals("Jim Business", blog.getTitle());
343       assertEquals(2, blog.getPosts().size());
344       assertEquals("Corn nuts", blog.getPosts().get(0).getSubject());
345       assertEquals(101, blog.getAuthor().getId());
346       assertEquals("jim", blog.getAuthor().getUsername());
347     }
348   }
349 
350   @Test
351   void shouldSelectBlogWithPostsAndAuthorUsingJoin() {
352     try (SqlSession session = sqlMapper.openSession()) {
353       Blog blog = session.selectOne("org.apache.ibatis.domain.blog.mappers.BlogMapper.selectBlogJoinedWithPostsAndAuthor", 1);
354       assertEquals("Jim Business", blog.getTitle());
355 
356       final Author author = blog.getAuthor();
357       assertEquals(101, author.getId());
358       assertEquals("jim", author.getUsername());
359 
360       final List<Post> posts = blog.getPosts();
361       assertEquals(2, posts.size());
362 
363       final Post post = blog.getPosts().get(0);
364       assertEquals(1, post.getId());
365       assertEquals("Corn nuts", post.getSubject());
366 
367       final List<Comment> comments = post.getComments();
368       assertEquals(2, comments.size());
369 
370       final List<Tag> tags = post.getTags();
371       assertEquals(3, tags.size());
372 
373       final Comment comment = comments.get(0);
374       assertEquals(1, comment.getId());
375 
376       assertEquals(DraftPost.class, blog.getPosts().get(0).getClass());
377       assertEquals(Post.class, blog.getPosts().get(1).getClass());
378     }
379   }
380 
381   @Test
382   void shouldSelectNestedBlogWithPostsAndAuthorUsingJoin() {
383     try (SqlSession session = sqlMapper.openSession()) {
384       Blog blog = session.selectOne("org.apache.ibatis.domain.blog.mappers.NestedBlogMapper.selectBlogJoinedWithPostsAndAuthor", 1);
385       assertEquals("Jim Business", blog.getTitle());
386 
387       final Author author = blog.getAuthor();
388       assertEquals(101, author.getId());
389       assertEquals("jim", author.getUsername());
390 
391       final List<Post> posts = blog.getPosts();
392       assertEquals(2, posts.size());
393 
394       final Post post = blog.getPosts().get(0);
395       assertEquals(1, post.getId());
396       assertEquals("Corn nuts", post.getSubject());
397 
398       final List<Comment> comments = post.getComments();
399       assertEquals(2, comments.size());
400 
401       final List<Tag> tags = post.getTags();
402       assertEquals(3, tags.size());
403 
404       final Comment comment = comments.get(0);
405       assertEquals(1, comment.getId());
406 
407       assertEquals(DraftPost.class, blog.getPosts().get(0).getClass());
408       assertEquals(Post.class, blog.getPosts().get(1).getClass());
409     }
410   }
411 
412   @Test
413   void shouldThrowExceptionIfMappedStatementDoesNotExist() {
414     try (SqlSession session = sqlMapper.openSession()) {
415       session.selectList("ThisStatementDoesNotExist");
416       fail("Expected exception to be thrown due to statement that does not exist.");
417     } catch (Exception e) {
418       assertTrue(e.getMessage().contains("does not contain value for ThisStatementDoesNotExist"));
419     }
420   }
421 
422   @Test
423   void shouldThrowExceptionIfTryingToAddStatementWithSameNameInXml() {
424     Configuration config = sqlMapper.getConfiguration();
425     try {
426       MappedStatement ms = new MappedStatement.Builder(config,
427           "org.apache.ibatis.domain.blog.mappers.BlogMapper.selectBlogWithPostsUsingSubSelect",
428           Mockito.mock(SqlSource.class), SqlCommandType.SELECT)
429               .resource("org/mybatis/TestMapper.xml").build();
430       config.addMappedStatement(ms);
431       fail("Expected exception to be thrown due to statement that already exists.");
432     } catch (Exception e) {
433       assertTrue(e.getMessage().contains("already contains value for org.apache.ibatis.domain.blog.mappers.BlogMapper.selectBlogWithPostsUsingSubSelect. please check org/apache/ibatis/builder/BlogMapper.xml and org/mybatis/TestMapper.xml"));
434     }
435   }
436 
437   @Test
438   void shouldThrowExceptionIfTryingToAddStatementWithSameNameInAnnotation() {
439     Configuration config = sqlMapper.getConfiguration();
440     try {
441       MappedStatement ms = new MappedStatement.Builder(config,
442           "org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor2",
443           Mockito.mock(SqlSource.class), SqlCommandType.SELECT)
444               .resource("org/mybatis/TestMapper.xml").build();
445       config.addMappedStatement(ms);
446       fail("Expected exception to be thrown due to statement that already exists.");
447     } catch (Exception e) {
448       assertTrue(e.getMessage().contains("already contains value for org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor2. please check org/apache/ibatis/domain/blog/mappers/AuthorMapper.java (best guess) and org/mybatis/TestMapper.xml"));
449     }
450   }
451 
452   @Test
453   void shouldCacheAllAuthors() {
454     int first;
455     try (SqlSession session = sqlMapper.openSession()) {
456       List<Author> authors = session.selectList("org.apache.ibatis.builder.CachedAuthorMapper.selectAllAuthors");
457       first = System.identityHashCode(authors);
458       session.commit(); // commit should not be required for read/only activity.
459     }
460     int second;
461     try (SqlSession session = sqlMapper.openSession()) {
462       List<Author> authors = session.selectList("org.apache.ibatis.builder.CachedAuthorMapper.selectAllAuthors");
463       second = System.identityHashCode(authors);
464     }
465     assertEquals(first, second);
466   }
467 
468   @Test
469   void shouldNotCacheAllAuthors() {
470     int first;
471     try (SqlSession session = sqlMapper.openSession()) {
472       List<Author> authors = session.selectList("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAllAuthors");
473       first = System.identityHashCode(authors);
474     }
475     int second;
476     try (SqlSession session = sqlMapper.openSession()) {
477       List<Author> authors = session.selectList("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAllAuthors");
478       second = System.identityHashCode(authors);
479     }
480     assertTrue(first != second);
481   }
482 
483   @Test
484   void shouldSelectAuthorsUsingMapperClass() {
485     try (SqlSession session = sqlMapper.openSession()) {
486       AuthorMapper mapper = session.getMapper(AuthorMapper.class);
487       List<Author> authors = mapper.selectAllAuthors();
488       assertEquals(2, authors.size());
489     }
490   }
491 
492   @Test
493   void shouldExecuteSelectOneAuthorUsingMapperClass() {
494     try (SqlSession session = sqlMapper.openSession()) {
495       AuthorMapper mapper = session.getMapper(AuthorMapper.class);
496       Author author = mapper.selectAuthor(101);
497       assertEquals(101, author.getId());
498     }
499   }
500 
501 
502   @Test
503   void shouldExecuteSelectOneAuthorUsingMapperClassThatReturnsALinedHashMap() {
504     try (SqlSession session = sqlMapper.openSession()) {
505       AuthorMapper mapper = session.getMapper(AuthorMapper.class);
506       LinkedHashMap<String, Object> author = mapper.selectAuthorLinkedHashMap(101);
507       assertEquals(101, author.get("ID"));
508     }
509   }
510 
511   @Test
512   void shouldExecuteSelectAllAuthorsUsingMapperClassThatReturnsSet() {
513     try (SqlSession session = sqlMapper.openSession()) {
514       AuthorMapper mapper = session.getMapper(AuthorMapper.class);
515       Collection<Author> authors = mapper.selectAllAuthorsSet();
516       assertEquals(2, authors.size());
517     }
518   }
519 
520   @Test
521   void shouldExecuteSelectAllAuthorsUsingMapperClassThatReturnsVector() {
522     try (SqlSession session = sqlMapper.openSession()) {
523       AuthorMapper mapper = session.getMapper(AuthorMapper.class);
524       Collection<Author> authors = mapper.selectAllAuthorsVector();
525       assertEquals(2, authors.size());
526     }
527   }
528 
529   @Test
530   void shouldExecuteSelectAllAuthorsUsingMapperClassThatReturnsLinkedList() {
531     try (SqlSession session = sqlMapper.openSession()) {
532       AuthorMapper mapper = session.getMapper(AuthorMapper.class);
533       Collection<Author> authors = mapper.selectAllAuthorsLinkedList();
534       assertEquals(2, authors.size());
535     }
536   }
537 
538   @Test
539   void shouldExecuteSelectAllAuthorsUsingMapperClassThatReturnsAnArray() {
540     try (SqlSession session = sqlMapper.openSession()) {
541       AuthorMapper mapper = session.getMapper(AuthorMapper.class);
542       Author[] authors = mapper.selectAllAuthorsArray();
543       assertEquals(2, authors.length);
544     }
545   }
546 
547   @Test
548   void shouldExecuteSelectOneAuthorUsingMapperClassWithResultHandler() {
549     try (SqlSession session = sqlMapper.openSession()) {
550       DefaultResultHandler handler = new DefaultResultHandler();
551       AuthorMapper mapper = session.getMapper(AuthorMapper.class);
552       mapper.selectAuthor(101, handler);
553       Author author = (Author) handler.getResultList().get(0);
554       assertEquals(101, author.getId());
555     }
556   }
557 
558   @Test
559   void shouldFailExecutingAnAnnotatedMapperClassWithResultHandler() {
560     try (SqlSession session = sqlMapper.openSession()) {
561       DefaultResultHandler handler = new DefaultResultHandler();
562       AuthorMapper mapper = session.getMapper(AuthorMapper.class);
563       Assertions.assertThrows(BindingException.class, () -> {
564         mapper.selectAuthor2(101, handler);
565       });
566     }
567   }
568 
569   @Test
570   void shouldSelectAuthorsUsingMapperClassWithResultHandler() {
571     try (SqlSession session = sqlMapper.openSession()) {
572       DefaultResultHandler handler = new DefaultResultHandler();
573       AuthorMapper mapper = session.getMapper(AuthorMapper.class);
574       mapper.selectAllAuthors(handler);
575       assertEquals(2, handler.getResultList().size());
576     }
577   }
578 
579   @Test
580   void shouldFailSelectOneAuthorUsingMapperClassWithTwoResultHandlers() {
581     Configuration configuration = new Configuration(sqlMapper.getConfiguration().getEnvironment());
582     configuration.addMapper(AuthorMapperWithMultipleHandlers.class);
583     SqlSessionFactory sqlMapperWithMultipleHandlers = new DefaultSqlSessionFactory(configuration);
584     try (SqlSession sqlSession = sqlMapperWithMultipleHandlers.openSession();) {
585       DefaultResultHandler handler1 = new DefaultResultHandler();
586       DefaultResultHandler handler2 = new DefaultResultHandler();
587       AuthorMapperWithMultipleHandlers mapper = sqlSession.getMapper(AuthorMapperWithMultipleHandlers.class);
588       Assertions.assertThrows(BindingException.class, () -> mapper.selectAuthor(101, handler1, handler2));
589     }
590   }
591 
592   @Test
593   void shouldFailSelectOneAuthorUsingMapperClassWithTwoRowBounds() {
594     Configuration configuration = new Configuration(sqlMapper.getConfiguration().getEnvironment());
595     configuration.addMapper(AuthorMapperWithRowBounds.class);
596     SqlSessionFactory sqlMapperWithMultipleHandlers = new DefaultSqlSessionFactory(configuration);
597     try (SqlSession sqlSession = sqlMapperWithMultipleHandlers.openSession();) {
598       RowBounds bounds1 = new RowBounds(0, 1);
599       RowBounds bounds2 = new RowBounds(0, 1);
600       AuthorMapperWithRowBounds mapper = sqlSession.getMapper(AuthorMapperWithRowBounds.class);
601       Assertions.assertThrows(BindingException.class, () -> mapper.selectAuthor(101, bounds1, bounds2));
602     }
603   }
604 
605   @Test
606   void shouldInsertAuthorUsingMapperClass() {
607     try (SqlSession session = sqlMapper.openSession()) {
608       AuthorMapper mapper = session.getMapper(AuthorMapper.class);
609       Author expected = new Author(500, "cbegin", "******", "cbegin@somewhere.com", "Something...", null);
610       mapper.insertAuthor(expected);
611       Author actual = mapper.selectAuthor(500);
612       assertNotNull(actual);
613       assertEquals(expected.getId(), actual.getId());
614       assertEquals(expected.getUsername(), actual.getUsername());
615       assertEquals(expected.getPassword(), actual.getPassword());
616       assertEquals(expected.getEmail(), actual.getEmail());
617       assertEquals(expected.getBio(), actual.getBio());
618     }
619   }
620 
621   @Test
622   void shouldDeleteAuthorUsingMapperClass() {
623     try (SqlSession session = sqlMapper.openSession()) {
624       AuthorMapper mapper = session.getMapper(AuthorMapper.class);
625       int count = mapper.deleteAuthor(101);
626       assertEquals(1, count);
627       assertNull(mapper.selectAuthor(101));
628     }
629   }
630 
631   @Test
632   void shouldUpdateAuthorUsingMapperClass() {
633     try (SqlSession session = sqlMapper.openSession()) {
634       AuthorMapper mapper = session.getMapper(AuthorMapper.class);
635       Author expected = mapper.selectAuthor(101);
636       expected.setUsername("NewUsername");
637       int count = mapper.updateAuthor(expected);
638       assertEquals(1, count);
639       Author actual = mapper.selectAuthor(101);
640       assertEquals(expected.getUsername(), actual.getUsername());
641     }
642   }
643 
644   @Test
645   void shouldSelectAllPostsUsingMapperClass() {
646     try (SqlSession session = sqlMapper.openSession()) {
647       BlogMapper mapper = session.getMapper(BlogMapper.class);
648       List<Map> posts = mapper.selectAllPosts();
649       assertEquals(5, posts.size());
650     }
651   }
652 
653   @Test
654   void shouldLimitResultsUsingMapperClass() {
655     try (SqlSession session = sqlMapper.openSession()) {
656       BlogMapper mapper = session.getMapper(BlogMapper.class);
657       List<Map> posts = mapper.selectAllPosts(new RowBounds(0, 2), null);
658       assertEquals(2, posts.size());
659       assertEquals(1, posts.get(0).get("ID"));
660       assertEquals(2, posts.get(1).get("ID"));
661     }
662   }
663 
664   private static class TestResultHandler implements ResultHandler {
665     int count = 0;
666     @Override
667     public void handleResult(ResultContext context) {
668       count++;
669     }
670   }
671 
672   @Test
673   void shouldHandleZeroParameters() {
674     try (SqlSession session = sqlMapper.openSession()) {
675       final TestResultHandler resultHandler = new TestResultHandler();
676       session.select("org.apache.ibatis.domain.blog.mappers.BlogMapper.selectAllPosts", resultHandler);
677       assertEquals(5, resultHandler.count);
678     }
679   }
680 
681   private static class TestResultStopHandler implements ResultHandler {
682     int count = 0;
683     @Override
684     public void handleResult(ResultContext context) {
685       count++;
686       if (count == 2) context.stop();
687     }
688   }
689 
690   @Test
691   void shouldStopResultHandler() {
692     try (SqlSession session = sqlMapper.openSession()) {
693       final TestResultStopHandler resultHandler = new TestResultStopHandler();
694       session.select("org.apache.ibatis.domain.blog.mappers.BlogMapper.selectAllPosts", null, resultHandler);
695       assertEquals(2, resultHandler.count);
696     }
697   }
698 
699   @Test
700   void shouldOffsetAndLimitResultsUsingMapperClass() {
701     try (SqlSession session = sqlMapper.openSession()) {
702       BlogMapper mapper = session.getMapper(BlogMapper.class);
703       List<Map> posts = mapper.selectAllPosts(new RowBounds(2, 3));
704       assertEquals(3, posts.size());
705       assertEquals(3, posts.get(0).get("ID"));
706       assertEquals(4, posts.get(1).get("ID"));
707       assertEquals(5, posts.get(2).get("ID"));
708     }
709   }
710 
711   @Test
712   void shouldFindPostsAllPostsWithDynamicSql() {
713     try (SqlSession session = sqlMapper.openSession()) {
714       List<Post> posts = session.selectList("org.apache.ibatis.domain.blog.mappers.PostMapper.findPost");
715       assertEquals(5, posts.size());
716     }
717   }
718 
719   @Test
720   void shouldFindPostByIDWithDynamicSql() {
721     try (SqlSession session = sqlMapper.openSession()) {
722       List<Post> posts = session.selectList("org.apache.ibatis.domain.blog.mappers.PostMapper.findPost",
723           new HashMap<String, Integer>() {{
724             put("id", 1);
725           }});
726       assertEquals(1, posts.size());
727     }
728   }
729 
730   @Test
731   void shouldFindPostsInSetOfIDsWithDynamicSql() {
732     try (SqlSession session = sqlMapper.openSession()) {
733       List<Post> posts = session.selectList("org.apache.ibatis.domain.blog.mappers.PostMapper.findPost",
734           new HashMap<String, List<Integer>>() {{
735             put("ids", new ArrayList<Integer>() {{
736               add(1);
737               add(2);
738               add(3);
739             }});
740           }});
741       assertEquals(3, posts.size());
742     }
743   }
744 
745   @Test
746   void shouldFindPostsWithBlogIdUsingDynamicSql() {
747     try (SqlSession session = sqlMapper.openSession()) {
748       List<Post> posts = session.selectList("org.apache.ibatis.domain.blog.mappers.PostMapper.findPost",
749           new HashMap<String, Integer>() {{
750             put("blog_id", 1);
751           }});
752       assertEquals(2, posts.size());
753     }
754   }
755 
756   @Test
757   void shouldFindPostsWithAuthorIdUsingDynamicSql() {
758     try (SqlSession session = sqlMapper.openSession()) {
759       List<Post> posts = session.selectList("org.apache.ibatis.domain.blog.mappers.PostMapper.findPost",
760           new HashMap<String, Integer>() {{
761             put("author_id", 101);
762           }});
763       assertEquals(3, posts.size());
764     }
765   }
766 
767   @Test
768   void shouldFindPostsWithAuthorAndBlogIdUsingDynamicSql() {
769     try (SqlSession session = sqlMapper.openSession()) {
770       List<Post> posts = session.selectList("org.apache.ibatis.domain.blog.mappers.PostMapper.findPost",
771           new HashMap<String, Object>() {{
772             put("ids", new ArrayList<Integer>() {{
773               add(1);
774               add(2);
775               add(3);
776             }});
777             put("blog_id", 1);
778           }});
779       assertEquals(2, posts.size());
780     }
781   }
782 
783   @Test
784   void shouldFindPostsInList() {
785     try (SqlSession session = sqlMapper.openSession()) {
786       List<Post> posts = session.selectList("org.apache.ibatis.domain.blog.mappers.PostMapper.selectPostIn",
787           new ArrayList<Integer>() {{
788             add(1);
789             add(3);
790             add(5);
791           }});
792       assertEquals(3, posts.size());
793     }
794   }
795 
796   @Test
797   void shouldFindOddPostsInList() {
798     try (SqlSession session = sqlMapper.openSession()) {
799       List<Post> posts = session.selectList("org.apache.ibatis.domain.blog.mappers.PostMapper.selectOddPostsIn",
800           new ArrayList<Integer>() {{
801             add(0);
802             add(1);
803             add(2);
804             add(3);
805             add(4);
806           }});
807       // we're getting odd indexes, not odd values, 0 is not odd
808       assertEquals(2, posts.size());
809       assertEquals(1, posts.get(0).getId());
810       assertEquals(3, posts.get(1).getId());
811     }
812   }
813 
814 
815   @Test
816   void shouldSelectOddPostsInKeysList() {
817     try (SqlSession session = sqlMapper.openSession()) {
818       List<Post> posts = session.selectList("org.apache.ibatis.domain.blog.mappers.PostMapper.selectOddPostsInKeysList",
819           new HashMap<String, List<Integer>>() {{put("keys",new ArrayList<Integer>() {{
820             add(0);
821             add(1);
822             add(2);
823             add(3);
824             add(4);
825           }});
826           }});
827       // we're getting odd indexes, not odd values, 0 is not odd
828       assertEquals(2, posts.size());
829       assertEquals(1, posts.get(0).getId());
830       assertEquals(3, posts.get(1).getId());
831     }
832   }
833 
834 }