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.executor;
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  
23  import java.util.ArrayList;
24  import java.util.HashMap;
25  import java.util.List;
26  import java.util.Map;
27  
28  import javassist.util.proxy.Proxy;
29  
30  import javax.sql.DataSource;
31  
32  import org.apache.ibatis.BaseDataTest;
33  import org.apache.ibatis.builder.StaticSqlSource;
34  import org.apache.ibatis.cache.CacheKey;
35  import org.apache.ibatis.domain.blog.Author;
36  import org.apache.ibatis.domain.blog.Blog;
37  import org.apache.ibatis.domain.blog.Post;
38  import org.apache.ibatis.domain.blog.Section;
39  import org.apache.ibatis.mapping.BoundSql;
40  import org.apache.ibatis.mapping.MappedStatement;
41  import org.apache.ibatis.mapping.ParameterMapping;
42  import org.apache.ibatis.mapping.SqlCommandType;
43  import org.apache.ibatis.session.Configuration;
44  import org.apache.ibatis.session.RowBounds;
45  import org.apache.ibatis.transaction.Transaction;
46  import org.apache.ibatis.transaction.jdbc.JdbcTransaction;
47  import org.apache.ibatis.type.JdbcType;
48  import org.apache.ibatis.type.TypeHandlerRegistry;
49  import org.junit.jupiter.api.BeforeAll;
50  import org.junit.jupiter.api.Test;
51  
52  class BaseExecutorTest extends BaseDataTest {
53    protected final Configuration config;
54    private static DataSource ds;
55  
56    @BeforeAll
57    static void setup() throws Exception {
58      ds = createBlogDataSource();
59    }
60  
61    BaseExecutorTest() {
62      config = new Configuration();
63      config.setLazyLoadingEnabled(true);
64      config.setUseGeneratedKeys(false);
65      config.setUseColumnLabel(true);
66      config.setDefaultStatementTimeout(5000);
67      config.setDefaultFetchSize(100);
68    }
69  
70    @Test
71    void shouldInsertNewAuthorWithBeforeAutoKey() throws Exception {
72  
73      Executor executor = createExecutor(new JdbcTransaction(ds, null, false));
74      try {
75        Author author = new Author(-1, "someone", "******", "someone@apache.org", null, Section.NEWS);
76        MappedStatement insertStatement = ExecutorTestHelper.prepareInsertAuthorMappedStatementWithBeforeAutoKey(config);
77        MappedStatement selectStatement = ExecutorTestHelper.prepareSelectOneAuthorMappedStatement(config);
78        int rows = executor.update(insertStatement, author);
79        assertTrue(rows > 0 || rows == BatchExecutor.BATCH_UPDATE_RETURN_VALUE);
80        if (rows == BatchExecutor.BATCH_UPDATE_RETURN_VALUE) {
81          executor.flushStatements();
82        }
83        assertEquals(123456, author.getId());
84        if (author.getId() != BatchExecutor.BATCH_UPDATE_RETURN_VALUE) {
85          List<Author> authors = executor.query(selectStatement, author.getId(), RowBounds.DEFAULT,
86              Executor.NO_RESULT_HANDLER);
87          executor.rollback(true);
88          assertEquals(1, authors.size());
89          assertEquals(author.toString(), authors.get(0).toString());
90          assertTrue(author.getId() >= 10000);
91        }
92      } finally {
93        executor.rollback(true);
94        executor.close(false);
95      }
96    }
97  
98    @Test
99    void shouldInsertNewAuthor() throws Exception {
100 
101     Executor executor = createExecutor(new JdbcTransaction(ds, null, false));
102     try {
103       Author author = new Author(99, "someone", "******", "someone@apache.org", null, Section.NEWS);
104       MappedStatement insertStatement = ExecutorTestHelper.prepareInsertAuthorMappedStatement(config);
105       MappedStatement selectStatement = ExecutorTestHelper.prepareSelectOneAuthorMappedStatement(config);
106       int rows = executor.update(insertStatement, author);
107       List<Author> authors = executor.query(selectStatement, 99, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
108       executor.flushStatements();
109       executor.rollback(true);
110       assertEquals(1, authors.size());
111       assertEquals(author.toString(), authors.get(0).toString());
112       assertTrue(1 == rows || BatchExecutor.BATCH_UPDATE_RETURN_VALUE == rows);
113     } finally {
114       executor.rollback(true);
115       executor.close(false);
116     }
117   }
118 
119   @Test
120   void shouldSelectAllAuthorsAutoMapped() throws Exception {
121 
122     Executor executor = createExecutor(new JdbcTransaction(ds, null, false));
123     try {
124       MappedStatement selectStatement = ExecutorTestHelper.prepareSelectAllAuthorsAutoMappedStatement(config);
125       List<Author> authors = executor.query(selectStatement, null, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
126       assertEquals(2, authors.size());
127       Author author = authors.get(0);
128       // id,username, password, email, bio, favourite_section
129       // (101,'jim','********','jim@ibatis.apache.org','','NEWS');
130       assertEquals(101, author.getId());
131       assertEquals("jim", author.getUsername());
132       assertEquals("jim@ibatis.apache.org", author.getEmail());
133       assertEquals("", author.getBio());
134       assertEquals(Section.NEWS, author.getFavouriteSection());
135     } finally {
136       executor.rollback(true);
137       executor.close(false);
138     }
139   }
140 
141   @Test
142   void shouldInsertNewAuthorWithAutoKey() throws Exception {
143 
144     Executor executor = createExecutor(new JdbcTransaction(ds, null, false));
145     try {
146       Author author = new Author(-1, "someone", "******", "someone@apache.org", null, Section.NEWS);
147       MappedStatement insertStatement = ExecutorTestHelper.prepareInsertAuthorMappedStatementWithAutoKey(config);
148       MappedStatement selectStatement = ExecutorTestHelper.prepareSelectOneAuthorMappedStatement(config);
149       int rows = executor.update(insertStatement, author);
150       assertTrue(rows > 0 || rows == BatchExecutor.BATCH_UPDATE_RETURN_VALUE);
151       if (rows == BatchExecutor.BATCH_UPDATE_RETURN_VALUE) {
152         executor.flushStatements();
153       }
154       assertTrue(-1 != author.getId());
155       if (author.getId() != BatchExecutor.BATCH_UPDATE_RETURN_VALUE) {
156         List<Author> authors = executor.query(selectStatement, author.getId(), RowBounds.DEFAULT,
157             Executor.NO_RESULT_HANDLER);
158         executor.rollback(true);
159         assertEquals(1, authors.size());
160         assertEquals(author.toString(), authors.get(0).toString());
161         assertTrue(author.getId() >= 10000);
162       }
163     } finally {
164       executor.rollback(true);
165       executor.close(false);
166     }
167   }
168 
169   @Test
170   void shouldInsertNewAuthorByProc() throws Exception {
171 
172     Executor executor = createExecutor(new JdbcTransaction(ds, null, false));
173     try {
174       Author author = new Author(97, "someone", "******", "someone@apache.org", null, null);
175       MappedStatement insertStatement = ExecutorTestHelper.prepareInsertAuthorProc(config);
176       MappedStatement selectStatement = ExecutorTestHelper.prepareSelectOneAuthorMappedStatement(config);
177       executor.update(insertStatement, author);
178       List<Author> authors = executor.query(selectStatement, 97, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
179       executor.flushStatements();
180       executor.rollback(true);
181       assertEquals(1, authors.size());
182       assertEquals(author.toString(), authors.get(0).toString());
183     } finally {
184       executor.rollback(true);
185       executor.close(false);
186     }
187   }
188 
189   @Test
190   void shouldInsertNewAuthorUsingSimpleNonPreparedStatements() throws Exception {
191 
192     Executor executor = createExecutor(new JdbcTransaction(ds, null, false));
193     try {
194       Author author = new Author(99, "someone", "******", "someone@apache.org", null, null);
195       MappedStatement insertStatement = ExecutorTestHelper.createInsertAuthorWithIDof99MappedStatement(config);
196       MappedStatement selectStatement = ExecutorTestHelper.createSelectAuthorWithIDof99MappedStatement(config);
197       int rows = executor.update(insertStatement, null);
198       List<Author> authors = executor.query(selectStatement, 99, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
199       executor.flushStatements();
200       executor.rollback(true);
201       assertEquals(1, authors.size());
202       assertEquals(author.toString(), authors.get(0).toString());
203       assertTrue(1 == rows || BatchExecutor.BATCH_UPDATE_RETURN_VALUE == rows);
204     } finally {
205       executor.rollback(true);
206       executor.close(false);
207     }
208   }
209 
210   @Test
211   void shouldUpdateAuthor() throws Exception {
212 
213     Executor executor = createExecutor(new JdbcTransaction(ds, null, false));
214     try {
215       Author author = new Author(101, "someone", "******", "someone@apache.org", null, Section.NEWS);
216       MappedStatement updateStatement = ExecutorTestHelper.prepareUpdateAuthorMappedStatement(config);
217       MappedStatement selectStatement = ExecutorTestHelper.prepareSelectOneAuthorMappedStatement(config);
218       int rows = executor.update(updateStatement, author);
219       List<Author> authors = executor.query(selectStatement, 101, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
220       executor.flushStatements();
221       executor.rollback(true);
222       assertEquals(1, authors.size());
223       assertEquals(author.toString(), authors.get(0).toString());
224       assertTrue(1 == rows || BatchExecutor.BATCH_UPDATE_RETURN_VALUE == rows);
225     } finally {
226       executor.rollback(true);
227       executor.close(false);
228     }
229   }
230 
231   @Test
232   void shouldDeleteAuthor() throws Exception {
233 
234     Executor executor = createExecutor(new JdbcTransaction(ds, null, false));
235     try {
236       Author author = new Author(101, null, null, null, null, null);
237       MappedStatement deleteStatement = ExecutorTestHelper.prepareDeleteAuthorMappedStatement(config);
238       MappedStatement selectStatement = ExecutorTestHelper.prepareSelectOneAuthorMappedStatement(config);
239       int rows = executor.update(deleteStatement, author);
240       List<Author> authors = executor.query(selectStatement, 101, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
241       executor.flushStatements();
242       executor.rollback(true);
243       assertEquals(0, authors.size());
244       assertTrue(1 == rows || BatchExecutor.BATCH_UPDATE_RETURN_VALUE == rows);
245     } finally {
246       executor.rollback(true);
247       executor.close(false);
248     }
249   }
250 
251   @Test
252   void shouldSelectDiscriminatedPost() throws Exception {
253 
254     Executor executor = createExecutor(new JdbcTransaction(ds, null, false));
255     try {
256       MappedStatement selectStatement = ExecutorTestHelper.prepareSelectDiscriminatedPost(config);
257       List<Map<String, String>> products = executor.query(selectStatement, null, RowBounds.DEFAULT,
258           Executor.NO_RESULT_HANDLER);
259       assertEquals(5, products.size());
260       for (Map<String, String> m : products) {
261         if ("IMAGES".equals(m.get("SECTION"))) {
262           assertNull(m.get("subject"));
263           assertNotNull(m.get("id"));
264         } else {
265           assertNotNull(m.get("subject"));
266           assertNull(m.get("id"));
267         }
268       }
269     } finally {
270       executor.close(false);
271     }
272   }
273 
274   @Test
275   void shouldSelect2DiscriminatedPosts() throws Exception {
276 
277     Executor executor = createExecutor(new JdbcTransaction(ds, null, false));
278     try {
279       MappedStatement selectStatement = ExecutorTestHelper.prepareSelectDiscriminatedPost(config);
280       List<Map<String, String>> products = executor.query(selectStatement, null, new RowBounds(2, 2),
281           Executor.NO_RESULT_HANDLER);
282       assertEquals(2, products.size());
283       for (Map<String, String> m : products) {
284         if ("IMAGES".equals(m.get("SECTION"))) {
285           assertNull(m.get("subject"));
286           assertNotNull(m.get("id"));
287         } else {
288           assertNotNull(m.get("subject"));
289           assertNull(m.get("id"));
290         }
291       }
292     } finally {
293       executor.rollback(true);
294       executor.close(false);
295     }
296   }
297 
298   @Test
299   void shouldSelectTwoSetsOfAuthorsViaProc() throws Exception {
300     Executor executor = createExecutor(new JdbcTransaction(ds, null, false));
301     try {
302       MappedStatement selectStatement = ExecutorTestHelper.prepareSelectTwoSetsOfAuthorsProc(config);
303       List<List<Author>> authorSets = executor.query(selectStatement, new HashMap<String, Object>() {
304         private static final long serialVersionUID = 1L;
305         {
306           put("id1", 101);
307           put("id2", 102);
308         }
309       }, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
310       assertEquals(2, authorSets.size());
311       for (List<Author> authors : authorSets) {
312         assertEquals(2, authors.size());
313         for (Object author : authors) {
314           assertTrue(author instanceof Author);
315         }
316       }
317     } finally {
318       executor.rollback(true);
319       executor.close(false);
320     }
321   }
322 
323   @Test
324   void shouldSelectAuthorViaOutParams() throws Exception {
325 
326     Executor executor = createExecutor(new JdbcTransaction(ds, null, false));
327     try {
328       MappedStatement selectStatement = ExecutorTestHelper.prepareSelectAuthorViaOutParams(config);
329       Author author = new Author(102, null, null, null, null, null);
330       executor.query(selectStatement, author, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
331       assertEquals("sally", author.getUsername());
332       assertEquals("********", author.getPassword());
333       assertEquals("sally@ibatis.apache.org", author.getEmail());
334       assertNull(author.getBio());
335     } catch (ExecutorException e) {
336       if (!(executor instanceof CachingExecutor)) {
337         throw e;
338       }
339       // TODO see issue #464. Fail is OK.
340       assertTrue(e.getMessage().contains("OUT params is not supported"));
341     } finally {
342       executor.rollback(true);
343       executor.close(false);
344     }
345   }
346 
347   @Test
348   void shouldFetchPostsForBlog() throws Exception {
349 
350     Executor executor = createExecutor(new JdbcTransaction(ds, null, false));
351     try {
352       MappedStatement selectBlog = ExecutorTestHelper.prepareComplexSelectBlogMappedStatement(config);
353       MappedStatement selectPosts = ExecutorTestHelper.prepareSelectPostsForBlogMappedStatement(config);
354       config.addMappedStatement(selectBlog);
355       config.addMappedStatement(selectPosts);
356       List<Post> posts = executor.query(selectPosts, 1, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
357       executor.flushStatements();
358       assertEquals(2, posts.size());
359       assertTrue(posts.get(1) instanceof Proxy);
360       assertNotNull(posts.get(1).getBlog());
361       assertEquals(1, posts.get(1).getBlog().getId());
362       executor.rollback(true);
363     } finally {
364       executor.rollback(true);
365       executor.close(false);
366     }
367   }
368 
369   @Test
370   void shouldFetchOneOrphanedPostWithNoBlog() throws Exception {
371 
372     Executor executor = createExecutor(new JdbcTransaction(ds, null, false));
373     try {
374       MappedStatement selectBlog = ExecutorTestHelper.prepareComplexSelectBlogMappedStatement(config);
375       MappedStatement selectPost = ExecutorTestHelper.prepareSelectPostMappedStatement(config);
376       config.addMappedStatement(selectBlog);
377       config.addMappedStatement(selectPost);
378       List<Post> posts = executor.query(selectPost, 5, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
379       executor.flushStatements();
380       executor.rollback(true);
381       assertEquals(1, posts.size());
382       Post post = posts.get(0);
383       assertNull(post.getBlog());
384     } finally {
385       executor.rollback(true);
386       executor.close(false);
387     }
388   }
389 
390   @Test
391   void shouldFetchPostWithBlogWithCompositeKey() throws Exception {
392 
393     Executor executor = createExecutor(new JdbcTransaction(ds, null, false));
394     try {
395       MappedStatement selectBlog = ExecutorTestHelper.prepareSelectBlogByIdAndAuthor(config);
396       MappedStatement selectPost = ExecutorTestHelper.prepareSelectPostWithBlogByAuthorMappedStatement(config);
397       config.addMappedStatement(selectBlog);
398       config.addMappedStatement(selectPost);
399       List<Post> posts = executor.query(selectPost, 2, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
400       executor.flushStatements();
401       assertEquals(1, posts.size());
402       Post post = posts.get(0);
403       assertNotNull(post.getBlog());
404       assertEquals(101, post.getBlog().getAuthor().getId());
405       executor.rollback(true);
406     } finally {
407       executor.rollback(true);
408       executor.close(false);
409     }
410   }
411 
412   @Test
413   void shouldFetchComplexBlogs() throws Exception {
414 
415     Executor executor = createExecutor(new JdbcTransaction(ds, null, false));
416     try {
417       MappedStatement selectBlog = ExecutorTestHelper.prepareComplexSelectBlogMappedStatement(config);
418       MappedStatement selectPosts = ExecutorTestHelper.prepareSelectPostsForBlogMappedStatement(config);
419       config.addMappedStatement(selectBlog);
420       config.addMappedStatement(selectPosts);
421       List<Blog> blogs = executor.query(selectBlog, 1, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
422       executor.flushStatements();
423       assertEquals(1, blogs.size());
424       assertNotNull(blogs.get(0).getPosts());
425       assertEquals(2, blogs.get(0).getPosts().size());
426       assertEquals(1, blogs.get(0).getPosts().get(1).getBlog().getPosts().get(1).getBlog().getId());
427       executor.rollback(true);
428     } finally {
429       executor.rollback(true);
430       executor.close(false);
431     }
432   }
433 
434   @Test
435   void shouldMapConstructorResults() throws Exception {
436 
437     Executor executor = createExecutor(new JdbcTransaction(ds, null, false));
438     try {
439       MappedStatement selectStatement = ExecutorTestHelper
440           .prepareSelectOneAuthorMappedStatementWithConstructorResults(config);
441       List<Author> authors = executor.query(selectStatement, 102, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
442       executor.flushStatements();
443       executor.rollback(true);
444       assertEquals(1, authors.size());
445 
446       Author author = authors.get(0);
447       assertEquals(102, author.getId());
448     } finally {
449       executor.rollback(true);
450       executor.close(false);
451     }
452   }
453 
454   @Test
455   void shouldClearDeferredLoads() throws Exception {
456 
457     Executor executor = createExecutor(new JdbcTransaction(ds, null, false));
458     try {
459       MappedStatement selectBlog = ExecutorTestHelper.prepareComplexSelectBlogMappedStatement(config);
460       MappedStatement selectPosts = ExecutorTestHelper.prepareSelectPostsForBlogMappedStatement(config);
461       config.addMappedStatement(selectBlog);
462       config.addMappedStatement(selectPosts);
463       MappedStatement selectAuthor = ExecutorTestHelper.prepareSelectOneAuthorMappedStatement(config);
464       MappedStatement insertAuthor = ExecutorTestHelper.prepareInsertAuthorMappedStatement(config);
465 
466       // generate DeferredLoads
467       executor.query(selectPosts, 1, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
468 
469       Author author = new Author(-1, "someone", "******", "someone@apache.org", null, Section.NEWS);
470       executor.update(insertAuthor, author);
471       executor.query(selectAuthor, -1, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
472       executor.flushStatements();
473       executor.rollback(true);
474     } finally {
475       executor.rollback(true);
476       executor.close(false);
477     }
478   }
479 
480   @Test
481   void testCreateCacheKeyWithAdditionalParameter() {
482     TypeHandlerRegistry registry = config.getTypeHandlerRegistry();
483 
484     MappedStatement mappedStatement = new MappedStatement.Builder(config, "testSelect",
485         new StaticSqlSource(config, "some select statement"), SqlCommandType.SELECT).build();
486 
487     Object parameterObject = 1;
488 
489     BoundSql boundSql = new BoundSql(config, "some select statement", new ArrayList<ParameterMapping>() {
490       {
491         add(new ParameterMapping.Builder(config, "id", registry.getTypeHandler(int.class)).build());
492       }
493     }, parameterObject) {
494       {
495         setAdditionalParameter("id", 2);
496       }
497     };
498 
499     Executor executor = createExecutor(new JdbcTransaction(ds, null, false));
500     CacheKey cacheKey = executor.createCacheKey(mappedStatement, parameterObject, RowBounds.DEFAULT, boundSql);
501 
502     CacheKey expected = new CacheKey();
503     expected.update(mappedStatement.getId());
504     expected.update(RowBounds.DEFAULT.getOffset());
505     expected.update(RowBounds.DEFAULT.getLimit());
506     expected.update(boundSql.getSql());
507     expected.update(2);
508 
509     assertEquals(expected, cacheKey);
510   }
511 
512   @Test
513   void testCreateCacheKeyWithNull() {
514     TypeHandlerRegistry registry = config.getTypeHandlerRegistry();
515 
516     MappedStatement mappedStatement = new MappedStatement.Builder(config, "testSelect",
517         new StaticSqlSource(config, "some select statement"), SqlCommandType.SELECT).build();
518 
519     Object parameterObject = null;
520 
521     BoundSql boundSql = new BoundSql(config, "some select statement", new ArrayList<ParameterMapping>() {
522       {
523         add(new ParameterMapping.Builder(config, "id", registry.getTypeHandler(int.class)).build());
524       }
525     }, parameterObject);
526 
527     Executor executor = createExecutor(new JdbcTransaction(ds, null, false));
528     CacheKey cacheKey = executor.createCacheKey(mappedStatement, parameterObject, RowBounds.DEFAULT, boundSql);
529 
530     CacheKey expected = new CacheKey();
531     expected.update(mappedStatement.getId());
532     expected.update(RowBounds.DEFAULT.getOffset());
533     expected.update(RowBounds.DEFAULT.getLimit());
534     expected.update(boundSql.getSql());
535     expected.update(null);
536 
537     assertEquals(expected, cacheKey);
538   }
539 
540   @Test
541   void testCreateCacheKeyWithTypeHandler() {
542     TypeHandlerRegistry registry = config.getTypeHandlerRegistry();
543 
544     MappedStatement mappedStatement = new MappedStatement.Builder(config, "testSelect",
545         new StaticSqlSource(config, "some select statement"), SqlCommandType.SELECT).build();
546 
547     Object parameterObject = 1;
548 
549     BoundSql boundSql = new BoundSql(config, "some select statement", new ArrayList<ParameterMapping>() {
550       {
551         add(new ParameterMapping.Builder(config, "id", registry.getTypeHandler(int.class)).build());
552       }
553     }, parameterObject);
554 
555     Executor executor = createExecutor(new JdbcTransaction(ds, null, false));
556     CacheKey cacheKey = executor.createCacheKey(mappedStatement, parameterObject, RowBounds.DEFAULT, boundSql);
557 
558     CacheKey expected = new CacheKey();
559     expected.update(mappedStatement.getId());
560     expected.update(RowBounds.DEFAULT.getOffset());
561     expected.update(RowBounds.DEFAULT.getLimit());
562     expected.update(boundSql.getSql());
563     expected.update(1);
564 
565     assertEquals(expected, cacheKey);
566   }
567 
568   @Test
569   void testCreateCacheKeyWithMetaObject() {
570     TypeHandlerRegistry registry = config.getTypeHandlerRegistry();
571 
572     MappedStatement mappedStatement = new MappedStatement.Builder(config, "testSelect",
573         new StaticSqlSource(config, "some select statement"), SqlCommandType.SELECT).build();
574 
575     Author parameterObject = new Author(-1, "cbegin", "******", "cbegin@nowhere.com", "N/A", Section.NEWS);
576 
577     BoundSql boundSql = new BoundSql(config, "some select statement", new ArrayList<ParameterMapping>() {
578       {
579         add(new ParameterMapping.Builder(config, "id", registry.getTypeHandler(int.class)).build());
580         add(new ParameterMapping.Builder(config, "username", registry.getTypeHandler(String.class)).build());
581         add(new ParameterMapping.Builder(config, "password", registry.getTypeHandler(String.class)).build());
582         add(new ParameterMapping.Builder(config, "email", registry.getTypeHandler(String.class)).build());
583         add(new ParameterMapping.Builder(config, "bio", registry.getTypeHandler(String.class))
584             .jdbcType(JdbcType.VARCHAR).build());
585         add(new ParameterMapping.Builder(config, "favouriteSection", registry.getTypeHandler(Section.class))
586             .jdbcType(JdbcType.VARCHAR).build());
587       }
588     }, parameterObject);
589 
590     Executor executor = createExecutor(new JdbcTransaction(ds, null, false));
591     CacheKey cacheKey = executor.createCacheKey(mappedStatement, parameterObject, RowBounds.DEFAULT, boundSql);
592 
593     CacheKey expected = new CacheKey();
594     expected.update(mappedStatement.getId());
595     expected.update(RowBounds.DEFAULT.getOffset());
596     expected.update(RowBounds.DEFAULT.getLimit());
597     expected.update(boundSql.getSql());
598     expected.update(parameterObject.getId());
599     expected.update(parameterObject.getUsername());
600     expected.update(parameterObject.getPassword());
601     expected.update(parameterObject.getEmail());
602     expected.update(parameterObject.getBio());
603     expected.update(parameterObject.getFavouriteSection());
604 
605     assertEquals(expected, cacheKey);
606   }
607 
608   protected Executor createExecutor(Transaction transaction) {
609     return new SimpleExecutor(config, transaction);
610   }
611 
612 }