View Javadoc
1   /*
2    *    Copyright 2009-2024 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.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 key"));
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   @Test
171   void shouldSelectAllAuthorsAsMap() {
172     try (SqlSession session = sqlMapper.openSession(TransactionIsolationLevel.SERIALIZABLE)) {
173       final Map<Integer, Author> authors = session
174           .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(
194         configuration.getResultMap("org.apache.ibatis.domain.blog.mappers.BlogMapper.earlyNestedDiscriminatorPost")
195             .hasNestedResultMaps());
196     assertTrue(
197         configuration.getResultMap("org.apache.ibatis.domain.blog.mappers.BlogMapper.lateNestedDiscriminatorPost")
198             .hasNestedResultMaps());
199   }
200 
201   @Test
202   void shouldSelectOneAuthor() {
203     try (SqlSession session = sqlMapper.openSession()) {
204       Author author = session.selectOne("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor",
205           new Author(101));
206       assertEquals(101, author.getId());
207       assertEquals(Section.NEWS, author.getFavouriteSection());
208     }
209   }
210 
211   @Test
212   void shouldSelectOneAuthorAsList() {
213     try (SqlSession session = sqlMapper.openSession()) {
214       List<Author> authors = session.selectList("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor",
215           new Author(101));
216       assertEquals(101, authors.get(0).getId());
217       assertEquals(Section.NEWS, authors.get(0).getFavouriteSection());
218     }
219   }
220 
221   @Test
222   void shouldSelectOneImmutableAuthor() {
223     try (SqlSession session = sqlMapper.openSession()) {
224       ImmutableAuthor author = session
225           .selectOne("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectImmutableAuthor", new Author(101));
226       assertEquals(101, author.getId());
227       assertEquals(Section.NEWS, author.getFavouriteSection());
228     }
229   }
230 
231   @Test
232   void shouldSelectOneAuthorWithInlineParams() {
233     try (SqlSession session = sqlMapper.openSession()) {
234       Author author = session.selectOne(
235           "org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthorWithInlineParams", new Author(101));
236       assertEquals(101, author.getId());
237     }
238   }
239 
240   @Test
241   void shouldInsertAuthor() {
242     try (SqlSession session = sqlMapper.openSession()) {
243       Author expected = new Author(500, "cbegin", "******", "cbegin@somewhere.com", "Something...", null);
244       int updates = session.insert("org.apache.ibatis.domain.blog.mappers.AuthorMapper.insertAuthor", expected);
245       assertEquals(1, updates);
246       Author actual = session.selectOne("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor",
247           new Author(500));
248       assertNotNull(actual);
249       assertEquals(expected.getId(), actual.getId());
250       assertEquals(expected.getUsername(), actual.getUsername());
251       assertEquals(expected.getPassword(), actual.getPassword());
252       assertEquals(expected.getEmail(), actual.getEmail());
253       assertEquals(expected.getBio(), actual.getBio());
254     }
255   }
256 
257   @Test
258   void shouldUpdateAuthorImplicitRollback() {
259     try (SqlSession session = sqlMapper.openSession()) {
260       Author original = session.selectOne("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor", 101);
261       original.setEmail("new@email.com");
262       int updates = session.update("org.apache.ibatis.domain.blog.mappers.AuthorMapper.updateAuthor", original);
263       assertEquals(1, updates);
264       Author updated = session.selectOne("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor", 101);
265       assertEquals(original.getEmail(), updated.getEmail());
266     }
267     try (SqlSession session = sqlMapper.openSession()) {
268       Author updated = session.selectOne("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor", 101);
269       assertEquals("jim@ibatis.apache.org", updated.getEmail());
270     }
271   }
272 
273   @Test
274   void shouldUpdateAuthorCommit() {
275     Author original;
276     try (SqlSession session = sqlMapper.openSession()) {
277       original = session.selectOne("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor", 101);
278       original.setEmail("new@email.com");
279       int updates = session.update("org.apache.ibatis.domain.blog.mappers.AuthorMapper.updateAuthor", original);
280       assertEquals(1, updates);
281       Author updated = session.selectOne("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor", 101);
282       assertEquals(original.getEmail(), updated.getEmail());
283       session.commit();
284     }
285     try (SqlSession session = sqlMapper.openSession()) {
286       Author updated = session.selectOne("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor", 101);
287       assertEquals(original.getEmail(), updated.getEmail());
288     }
289   }
290 
291   @Test
292   void shouldUpdateAuthorIfNecessary() {
293     Author original;
294     try (SqlSession session = sqlMapper.openSession()) {
295       original = session.selectOne("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor", 101);
296       original.setEmail("new@email.com");
297       original.setBio(null);
298       int updates = session.update("org.apache.ibatis.domain.blog.mappers.AuthorMapper.updateAuthorIfNecessary",
299           original);
300       assertEquals(1, updates);
301       Author updated = session.selectOne("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor", 101);
302       assertEquals(original.getEmail(), updated.getEmail());
303       session.commit();
304     }
305     try (SqlSession session = sqlMapper.openSession()) {
306       Author updated = session.selectOne("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor", 101);
307       assertEquals(original.getEmail(), updated.getEmail());
308     }
309   }
310 
311   @Test
312   void shouldDeleteAuthor() {
313     try (SqlSession session = sqlMapper.openSession()) {
314       final int id = 102;
315 
316       List<Author> authors = session.selectList("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor", id);
317       assertEquals(1, authors.size());
318 
319       int updates = session.delete("org.apache.ibatis.domain.blog.mappers.AuthorMapper.deleteAuthor", id);
320       assertEquals(1, updates);
321 
322       authors = session.selectList("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor", id);
323       assertEquals(0, authors.size());
324 
325       session.rollback();
326       authors = session.selectList("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor", id);
327       assertEquals(1, authors.size());
328     }
329   }
330 
331   @Test
332   void shouldSelectBlogWithPostsAndAuthorUsingSubSelects() {
333     try (SqlSession session = sqlMapper.openSession()) {
334       Blog blog = session
335           .selectOne("org.apache.ibatis.domain.blog.mappers.BlogMapper.selectBlogWithPostsUsingSubSelect", 1);
336       assertEquals("Jim Business", blog.getTitle());
337       assertEquals(2, blog.getPosts().size());
338       assertEquals("Corn nuts", blog.getPosts().get(0).getSubject());
339       assertEquals(101, blog.getAuthor().getId());
340       assertEquals("jim", blog.getAuthor().getUsername());
341     }
342   }
343 
344   @Test
345   void shouldSelectBlogWithPostsAndAuthorUsingSubSelectsLazily() {
346     try (SqlSession session = sqlMapper.openSession()) {
347       Blog blog = session
348           .selectOne("org.apache.ibatis.domain.blog.mappers.BlogMapper.selectBlogWithPostsUsingSubSelectLazily", 1);
349       Assertions.assertTrue(blog instanceof Proxy);
350       assertEquals("Jim Business", blog.getTitle());
351       assertEquals(2, blog.getPosts().size());
352       assertEquals("Corn nuts", blog.getPosts().get(0).getSubject());
353       assertEquals(101, blog.getAuthor().getId());
354       assertEquals("jim", blog.getAuthor().getUsername());
355     }
356   }
357 
358   @Test
359   void shouldSelectBlogWithPostsAndAuthorUsingJoin() {
360     try (SqlSession session = sqlMapper.openSession()) {
361       Blog blog = session
362           .selectOne("org.apache.ibatis.domain.blog.mappers.BlogMapper.selectBlogJoinedWithPostsAndAuthor", 1);
363       assertEquals("Jim Business", blog.getTitle());
364 
365       final Author author = blog.getAuthor();
366       assertEquals(101, author.getId());
367       assertEquals("jim", author.getUsername());
368 
369       final List<Post> posts = blog.getPosts();
370       assertEquals(2, posts.size());
371 
372       final Post post = blog.getPosts().get(0);
373       assertEquals(1, post.getId());
374       assertEquals("Corn nuts", post.getSubject());
375 
376       final List<Comment> comments = post.getComments();
377       assertEquals(2, comments.size());
378 
379       final List<Tag> tags = post.getTags();
380       assertEquals(3, tags.size());
381 
382       final Comment comment = comments.get(0);
383       assertEquals(1, comment.getId());
384 
385       assertEquals(DraftPost.class, blog.getPosts().get(0).getClass());
386       assertEquals(Post.class, blog.getPosts().get(1).getClass());
387     }
388   }
389 
390   @Test
391   void shouldSelectNestedBlogWithPostsAndAuthorUsingJoin() {
392     try (SqlSession session = sqlMapper.openSession()) {
393       Blog blog = session
394           .selectOne("org.apache.ibatis.domain.blog.mappers.NestedBlogMapper.selectBlogJoinedWithPostsAndAuthor", 1);
395       assertEquals("Jim Business", blog.getTitle());
396 
397       final Author author = blog.getAuthor();
398       assertEquals(101, author.getId());
399       assertEquals("jim", author.getUsername());
400 
401       final List<Post> posts = blog.getPosts();
402       assertEquals(2, posts.size());
403 
404       final Post post = blog.getPosts().get(0);
405       assertEquals(1, post.getId());
406       assertEquals("Corn nuts", post.getSubject());
407 
408       final List<Comment> comments = post.getComments();
409       assertEquals(2, comments.size());
410 
411       final List<Tag> tags = post.getTags();
412       assertEquals(3, tags.size());
413 
414       final Comment comment = comments.get(0);
415       assertEquals(1, comment.getId());
416 
417       assertEquals(DraftPost.class, blog.getPosts().get(0).getClass());
418       assertEquals(Post.class, blog.getPosts().get(1).getClass());
419     }
420   }
421 
422   @Test
423   void shouldThrowExceptionIfMappedStatementDoesNotExist() {
424     try (SqlSession session = sqlMapper.openSession()) {
425       session.selectList("ThisStatementDoesNotExist");
426       fail("Expected exception to be thrown due to statement that does not exist.");
427     } catch (Exception e) {
428       assertTrue(e.getMessage().contains("does not contain value for ThisStatementDoesNotExist"));
429     }
430   }
431 
432   @Test
433   void shouldThrowExceptionIfTryingToAddStatementWithSameNameInXml() {
434     Configuration config = sqlMapper.getConfiguration();
435     try {
436       MappedStatement ms = new MappedStatement.Builder(config,
437           "org.apache.ibatis.domain.blog.mappers.BlogMapper.selectBlogWithPostsUsingSubSelect",
438           Mockito.mock(SqlSource.class), SqlCommandType.SELECT).resource("org/mybatis/TestMapper.xml").build();
439       config.addMappedStatement(ms);
440       fail("Expected exception to be thrown due to statement that already exists.");
441     } catch (Exception e) {
442       assertTrue(e.getMessage().contains(
443           "already contains key org.apache.ibatis.domain.blog.mappers.BlogMapper.selectBlogWithPostsUsingSubSelect. please check org/apache/ibatis/builder/BlogMapper.xml and org/mybatis/TestMapper.xml"));
444     }
445   }
446 
447   @Test
448   void shouldThrowExceptionIfTryingToAddStatementWithSameNameInAnnotation() {
449     Configuration config = sqlMapper.getConfiguration();
450     try {
451       MappedStatement ms = new MappedStatement.Builder(config,
452           "org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAuthor2", Mockito.mock(SqlSource.class),
453           SqlCommandType.SELECT).resource("org/mybatis/TestMapper.xml").build();
454       config.addMappedStatement(ms);
455       fail("Expected exception to be thrown due to statement that already exists.");
456     } catch (Exception e) {
457       assertTrue(e.getMessage().contains(
458           "already contains key 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"));
459     }
460   }
461 
462   @Test
463   void shouldCacheAllAuthors() {
464     int first;
465     try (SqlSession session = sqlMapper.openSession()) {
466       List<Author> authors = session.selectList("org.apache.ibatis.builder.CachedAuthorMapper.selectAllAuthors");
467       first = System.identityHashCode(authors);
468       session.commit(); // commit should not be required for read/only activity.
469     }
470     int second;
471     try (SqlSession session = sqlMapper.openSession()) {
472       List<Author> authors = session.selectList("org.apache.ibatis.builder.CachedAuthorMapper.selectAllAuthors");
473       second = System.identityHashCode(authors);
474     }
475     assertEquals(first, second);
476   }
477 
478   @Test
479   void shouldNotCacheAllAuthors() {
480     int first;
481     try (SqlSession session = sqlMapper.openSession()) {
482       List<Author> authors = session.selectList("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAllAuthors");
483       first = System.identityHashCode(authors);
484     }
485     int second;
486     try (SqlSession session = sqlMapper.openSession()) {
487       List<Author> authors = session.selectList("org.apache.ibatis.domain.blog.mappers.AuthorMapper.selectAllAuthors");
488       second = System.identityHashCode(authors);
489     }
490     assertTrue(first != second);
491   }
492 
493   @Test
494   void shouldSelectAuthorsUsingMapperClass() {
495     try (SqlSession session = sqlMapper.openSession()) {
496       AuthorMapper mapper = session.getMapper(AuthorMapper.class);
497       List<Author> authors = mapper.selectAllAuthors();
498       assertEquals(2, authors.size());
499     }
500   }
501 
502   @Test
503   void shouldExecuteSelectOneAuthorUsingMapperClass() {
504     try (SqlSession session = sqlMapper.openSession()) {
505       AuthorMapper mapper = session.getMapper(AuthorMapper.class);
506       Author author = mapper.selectAuthor(101);
507       assertEquals(101, author.getId());
508     }
509   }
510 
511   @Test
512   void shouldExecuteSelectOneAuthorUsingMapperClassThatReturnsALinedHashMap() {
513     try (SqlSession session = sqlMapper.openSession()) {
514       AuthorMapper mapper = session.getMapper(AuthorMapper.class);
515       LinkedHashMap<String, Object> author = mapper.selectAuthorLinkedHashMap(101);
516       assertEquals(101, author.get("ID"));
517     }
518   }
519 
520   @Test
521   void shouldExecuteSelectAllAuthorsUsingMapperClassThatReturnsSet() {
522     try (SqlSession session = sqlMapper.openSession()) {
523       AuthorMapper mapper = session.getMapper(AuthorMapper.class);
524       Collection<Author> authors = mapper.selectAllAuthorsSet();
525       assertEquals(2, authors.size());
526     }
527   }
528 
529   @Test
530   void shouldExecuteSelectAllAuthorsUsingMapperClassThatReturnsVector() {
531     try (SqlSession session = sqlMapper.openSession()) {
532       AuthorMapper mapper = session.getMapper(AuthorMapper.class);
533       Collection<Author> authors = mapper.selectAllAuthorsVector();
534       assertEquals(2, authors.size());
535     }
536   }
537 
538   @Test
539   void shouldExecuteSelectAllAuthorsUsingMapperClassThatReturnsLinkedList() {
540     try (SqlSession session = sqlMapper.openSession()) {
541       AuthorMapper mapper = session.getMapper(AuthorMapper.class);
542       Collection<Author> authors = mapper.selectAllAuthorsLinkedList();
543       assertEquals(2, authors.size());
544     }
545   }
546 
547   @Test
548   void shouldExecuteSelectAllAuthorsUsingMapperClassThatReturnsAnArray() {
549     try (SqlSession session = sqlMapper.openSession()) {
550       AuthorMapper mapper = session.getMapper(AuthorMapper.class);
551       Author[] authors = mapper.selectAllAuthorsArray();
552       assertEquals(2, authors.length);
553     }
554   }
555 
556   @Test
557   void shouldExecuteSelectOneAuthorUsingMapperClassWithResultHandler() {
558     try (SqlSession session = sqlMapper.openSession()) {
559       DefaultResultHandler handler = new DefaultResultHandler();
560       AuthorMapper mapper = session.getMapper(AuthorMapper.class);
561       mapper.selectAuthor(101, handler);
562       Author author = (Author) handler.getResultList().get(0);
563       assertEquals(101, author.getId());
564     }
565   }
566 
567   @Test
568   void shouldFailExecutingAnAnnotatedMapperClassWithResultHandler() {
569     try (SqlSession session = sqlMapper.openSession()) {
570       DefaultResultHandler handler = new DefaultResultHandler();
571       AuthorMapper mapper = session.getMapper(AuthorMapper.class);
572       Assertions.assertThrows(BindingException.class, () -> mapper.selectAuthor2(101, handler));
573     }
574   }
575 
576   @Test
577   void shouldSelectAuthorsUsingMapperClassWithResultHandler() {
578     try (SqlSession session = sqlMapper.openSession()) {
579       DefaultResultHandler handler = new DefaultResultHandler();
580       AuthorMapper mapper = session.getMapper(AuthorMapper.class);
581       mapper.selectAllAuthors(handler);
582       assertEquals(2, handler.getResultList().size());
583     }
584   }
585 
586   @Test
587   void shouldFailSelectOneAuthorUsingMapperClassWithTwoResultHandlers() {
588     Configuration configuration = new Configuration(sqlMapper.getConfiguration().getEnvironment());
589     configuration.addMapper(AuthorMapperWithMultipleHandlers.class);
590     SqlSessionFactory sqlMapperWithMultipleHandlers = new DefaultSqlSessionFactory(configuration);
591     try (SqlSession sqlSession = sqlMapperWithMultipleHandlers.openSession();) {
592       DefaultResultHandler handler1 = new DefaultResultHandler();
593       DefaultResultHandler handler2 = new DefaultResultHandler();
594       AuthorMapperWithMultipleHandlers mapper = sqlSession.getMapper(AuthorMapperWithMultipleHandlers.class);
595       Assertions.assertThrows(BindingException.class, () -> mapper.selectAuthor(101, handler1, handler2));
596     }
597   }
598 
599   @Test
600   void shouldFailSelectOneAuthorUsingMapperClassWithTwoRowBounds() {
601     Configuration configuration = new Configuration(sqlMapper.getConfiguration().getEnvironment());
602     configuration.addMapper(AuthorMapperWithRowBounds.class);
603     SqlSessionFactory sqlMapperWithMultipleHandlers = new DefaultSqlSessionFactory(configuration);
604     try (SqlSession sqlSession = sqlMapperWithMultipleHandlers.openSession();) {
605       RowBounds bounds1 = new RowBounds(0, 1);
606       RowBounds bounds2 = new RowBounds(0, 1);
607       AuthorMapperWithRowBounds mapper = sqlSession.getMapper(AuthorMapperWithRowBounds.class);
608       Assertions.assertThrows(BindingException.class, () -> mapper.selectAuthor(101, bounds1, bounds2));
609     }
610   }
611 
612   @Test
613   void shouldInsertAuthorUsingMapperClass() {
614     try (SqlSession session = sqlMapper.openSession()) {
615       AuthorMapper mapper = session.getMapper(AuthorMapper.class);
616       Author expected = new Author(500, "cbegin", "******", "cbegin@somewhere.com", "Something...", null);
617       mapper.insertAuthor(expected);
618       Author actual = mapper.selectAuthor(500);
619       assertNotNull(actual);
620       assertEquals(expected.getId(), actual.getId());
621       assertEquals(expected.getUsername(), actual.getUsername());
622       assertEquals(expected.getPassword(), actual.getPassword());
623       assertEquals(expected.getEmail(), actual.getEmail());
624       assertEquals(expected.getBio(), actual.getBio());
625     }
626   }
627 
628   @Test
629   void shouldDeleteAuthorUsingMapperClass() {
630     try (SqlSession session = sqlMapper.openSession()) {
631       AuthorMapper mapper = session.getMapper(AuthorMapper.class);
632       int count = mapper.deleteAuthor(101);
633       assertEquals(1, count);
634       assertNull(mapper.selectAuthor(101));
635     }
636   }
637 
638   @Test
639   void shouldUpdateAuthorUsingMapperClass() {
640     try (SqlSession session = sqlMapper.openSession()) {
641       AuthorMapper mapper = session.getMapper(AuthorMapper.class);
642       Author expected = mapper.selectAuthor(101);
643       expected.setUsername("NewUsername");
644       int count = mapper.updateAuthor(expected);
645       assertEquals(1, count);
646       Author actual = mapper.selectAuthor(101);
647       assertEquals(expected.getUsername(), actual.getUsername());
648     }
649   }
650 
651   @Test
652   void shouldSelectAllPostsUsingMapperClass() {
653     try (SqlSession session = sqlMapper.openSession()) {
654       BlogMapper mapper = session.getMapper(BlogMapper.class);
655       List<Map> posts = mapper.selectAllPosts();
656       assertEquals(5, posts.size());
657     }
658   }
659 
660   @Test
661   void shouldLimitResultsUsingMapperClass() {
662     try (SqlSession session = sqlMapper.openSession()) {
663       BlogMapper mapper = session.getMapper(BlogMapper.class);
664       List<Map> posts = mapper.selectAllPosts(new RowBounds(0, 2), null);
665       assertEquals(2, posts.size());
666       assertEquals(1, posts.get(0).get("ID"));
667       assertEquals(2, posts.get(1).get("ID"));
668     }
669   }
670 
671   private static class TestResultHandler implements ResultHandler {
672     int count;
673 
674     @Override
675     public void handleResult(ResultContext context) {
676       count++;
677     }
678   }
679 
680   @Test
681   void shouldHandleZeroParameters() {
682     try (SqlSession session = sqlMapper.openSession()) {
683       final TestResultHandler resultHandler = new TestResultHandler();
684       session.select("org.apache.ibatis.domain.blog.mappers.BlogMapper.selectAllPosts", resultHandler);
685       assertEquals(5, resultHandler.count);
686     }
687   }
688 
689   private static class TestResultStopHandler implements ResultHandler {
690     int count;
691 
692     @Override
693     public void handleResult(ResultContext context) {
694       count++;
695       if (count == 2) {
696         context.stop();
697       }
698     }
699   }
700 
701   @Test
702   void shouldStopResultHandler() {
703     try (SqlSession session = sqlMapper.openSession()) {
704       final TestResultStopHandler resultHandler = new TestResultStopHandler();
705       session.select("org.apache.ibatis.domain.blog.mappers.BlogMapper.selectAllPosts", null, resultHandler);
706       assertEquals(2, resultHandler.count);
707     }
708   }
709 
710   @Test
711   void shouldOffsetAndLimitResultsUsingMapperClass() {
712     try (SqlSession session = sqlMapper.openSession()) {
713       BlogMapper mapper = session.getMapper(BlogMapper.class);
714       List<Map> posts = mapper.selectAllPosts(new RowBounds(2, 3));
715       assertEquals(3, posts.size());
716       assertEquals(3, posts.get(0).get("ID"));
717       assertEquals(4, posts.get(1).get("ID"));
718       assertEquals(5, posts.get(2).get("ID"));
719     }
720   }
721 
722   @Test
723   void shouldFindPostsAllPostsWithDynamicSql() {
724     try (SqlSession session = sqlMapper.openSession()) {
725       List<Post> posts = session.selectList("org.apache.ibatis.domain.blog.mappers.PostMapper.findPost");
726       assertEquals(5, posts.size());
727     }
728   }
729 
730   @Test
731   void shouldFindPostByIDWithDynamicSql() {
732     try (SqlSession session = sqlMapper.openSession()) {
733       List<Post> posts = session.selectList("org.apache.ibatis.domain.blog.mappers.PostMapper.findPost",
734           new HashMap<String, Integer>() {
735             private static final long serialVersionUID = 1L;
736             {
737               put("id", 1);
738             }
739           });
740       assertEquals(1, posts.size());
741     }
742   }
743 
744   @Test
745   void shouldFindPostsInSetOfIDsWithDynamicSql() {
746     try (SqlSession session = sqlMapper.openSession()) {
747       List<Post> posts = session.selectList("org.apache.ibatis.domain.blog.mappers.PostMapper.findPost",
748           new HashMap<String, List<Integer>>() {
749             private static final long serialVersionUID = 1L;
750             {
751               put("ids", new ArrayList<Integer>() {
752                 private static final long serialVersionUID = 1L;
753                 {
754                   add(1);
755                   add(2);
756                   add(3);
757                 }
758               });
759             }
760           });
761       assertEquals(3, posts.size());
762     }
763   }
764 
765   @Test
766   void shouldFindPostsWithBlogIdUsingDynamicSql() {
767     try (SqlSession session = sqlMapper.openSession()) {
768       List<Post> posts = session.selectList("org.apache.ibatis.domain.blog.mappers.PostMapper.findPost",
769           new HashMap<String, Integer>() {
770             private static final long serialVersionUID = 1L;
771             {
772               put("blog_id", 1);
773             }
774           });
775       assertEquals(2, posts.size());
776     }
777   }
778 
779   @Test
780   void shouldFindPostsWithAuthorIdUsingDynamicSql() {
781     try (SqlSession session = sqlMapper.openSession()) {
782       List<Post> posts = session.selectList("org.apache.ibatis.domain.blog.mappers.PostMapper.findPost",
783           new HashMap<String, Integer>() {
784             private static final long serialVersionUID = 1L;
785             {
786               put("author_id", 101);
787             }
788           });
789       assertEquals(3, posts.size());
790     }
791   }
792 
793   @Test
794   void shouldFindPostsWithAuthorAndBlogIdUsingDynamicSql() {
795     try (SqlSession session = sqlMapper.openSession()) {
796       List<Post> posts = session.selectList("org.apache.ibatis.domain.blog.mappers.PostMapper.findPost",
797           new HashMap<String, Object>() {
798             private static final long serialVersionUID = 1L;
799             {
800               put("ids", new ArrayList<Integer>() {
801                 private static final long serialVersionUID = 1L;
802                 {
803                   add(1);
804                   add(2);
805                   add(3);
806                 }
807               });
808               put("blog_id", 1);
809             }
810           });
811       assertEquals(2, posts.size());
812     }
813   }
814 
815   @Test
816   void shouldFindPostsInList() {
817     try (SqlSession session = sqlMapper.openSession()) {
818       List<Post> posts = session.selectList("org.apache.ibatis.domain.blog.mappers.PostMapper.selectPostIn",
819           new ArrayList<Integer>() {
820             private static final long serialVersionUID = 1L;
821             {
822               add(1);
823               add(3);
824               add(5);
825             }
826           });
827       assertEquals(3, posts.size());
828     }
829   }
830 
831   @Test
832   void shouldFindOddPostsInList() {
833     try (SqlSession session = sqlMapper.openSession()) {
834       List<Post> posts = session.selectList("org.apache.ibatis.domain.blog.mappers.PostMapper.selectOddPostsIn",
835           new ArrayList<Integer>() {
836             private static final long serialVersionUID = 1L;
837             {
838               add(0);
839               add(1);
840               add(2);
841               add(3);
842               add(4);
843             }
844           });
845       // we're getting odd indexes, not odd values, 0 is not odd
846       assertEquals(2, posts.size());
847       assertEquals(1, posts.get(0).getId());
848       assertEquals(3, posts.get(1).getId());
849     }
850   }
851 
852   @Test
853   void shouldSelectOddPostsInKeysList() {
854     try (SqlSession session = sqlMapper.openSession()) {
855       List<Post> posts = session.selectList("org.apache.ibatis.domain.blog.mappers.PostMapper.selectOddPostsInKeysList",
856           new HashMap<String, List<Integer>>() {
857             private static final long serialVersionUID = 1L;
858             {
859               put("keys", new ArrayList<Integer>() {
860                 private static final long serialVersionUID = 1L;
861                 {
862                   add(0);
863                   add(1);
864                   add(2);
865                   add(3);
866                   add(4);
867                 }
868               });
869             }
870           });
871       // we're getting odd indexes, not odd values, 0 is not odd
872       assertEquals(2, posts.size());
873       assertEquals(1, posts.get(0).getId());
874       assertEquals(3, posts.get(1).getId());
875     }
876   }
877 
878 }