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