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.submitted.keygen;
17  
18  import static com.googlecode.catchexception.apis.BDDCatchException.caughtException;
19  import static com.googlecode.catchexception.apis.BDDCatchException.when;
20  import static org.assertj.core.api.BDDAssertions.then;
21  import static org.junit.jupiter.api.Assertions.assertEquals;
22  import static org.junit.jupiter.api.Assertions.assertNotNull;
23  
24  import java.io.Reader;
25  import java.util.ArrayList;
26  import java.util.Arrays;
27  import java.util.HashMap;
28  import java.util.HashSet;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Set;
32  
33  import org.apache.ibatis.BaseDataTest;
34  import org.apache.ibatis.exceptions.PersistenceException;
35  import org.apache.ibatis.io.Resources;
36  import org.apache.ibatis.session.ExecutorType;
37  import org.apache.ibatis.session.SqlSession;
38  import org.apache.ibatis.session.SqlSessionFactory;
39  import org.apache.ibatis.session.SqlSessionFactoryBuilder;
40  import org.junit.jupiter.api.BeforeAll;
41  import org.junit.jupiter.api.Test;
42  
43  /**
44   * @author liuzh
45   */
46  class Jdbc3KeyGeneratorTest {
47  
48    private static SqlSessionFactory sqlSessionFactory;
49  
50    @BeforeAll
51    static void setUp() throws Exception {
52      // create an SqlSessionFactory
53      try (Reader reader = Resources.getResourceAsReader("org/apache/ibatis/submitted/keygen/MapperConfig.xml")) {
54        sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
55      }
56  
57      // populate in-memory database
58      BaseDataTest.runScript(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(),
59          "org/apache/ibatis/submitted/keygen/CreateDB.sql");
60    }
61  
62    @Test
63    void shouldAssignKeyToBean() {
64      try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
65        try {
66          CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
67          Country country = new Country("China", "CN");
68          mapper.insertBean(country);
69          assertNotNull(country.getId());
70        } finally {
71          sqlSession.rollback();
72        }
73      }
74    }
75  
76    @Test
77    void shouldAssignKeyToBean_batch() {
78      try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
79        try {
80          CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
81          Country country1 = new Country("China", "CN");
82          mapper.insertBean(country1);
83          Country country2 = new Country("Canada", "CA");
84          mapper.insertBean(country2);
85          sqlSession.flushStatements();
86          sqlSession.clearCache();
87          assertNotNull(country1.getId());
88          assertNotNull(country2.getId());
89        } finally {
90          sqlSession.rollback();
91        }
92      }
93    }
94  
95    @Test
96    void shouldAssignKeyToNamedBean() {
97      try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
98        try {
99          CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
100         Country country = new Country("China", "CN");
101         mapper.insertNamedBean(country);
102         assertNotNull(country.getId());
103       } finally {
104         sqlSession.rollback();
105       }
106     }
107   }
108 
109   @Test
110   void shouldAssignKeyToNamedBean_batch() {
111     try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
112       try {
113         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
114         Country country1 = new Country("China", "CN");
115         mapper.insertNamedBean(country1);
116         Country country2 = new Country("Canada", "CA");
117         mapper.insertNamedBean(country2);
118         sqlSession.flushStatements();
119         sqlSession.clearCache();
120         assertNotNull(country1.getId());
121         assertNotNull(country2.getId());
122       } finally {
123         sqlSession.rollback();
124       }
125     }
126   }
127 
128   @Test
129   void shouldAssignKeyToNamedBean_keyPropertyWithParamName() {
130     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
131       try {
132         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
133         Country country = new Country("China", "CN");
134         mapper.insertNamedBean_keyPropertyWithParamName(country);
135         assertNotNull(country.getId());
136       } finally {
137         sqlSession.rollback();
138       }
139     }
140   }
141 
142   @Test
143   void shouldAssignKeyToNamedBean_keyPropertyWithParamName_batch() {
144     try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
145       try {
146         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
147         Country country1 = new Country("China", "CN");
148         mapper.insertNamedBean_keyPropertyWithParamName(country1);
149         Country country2 = new Country("Canada", "CA");
150         mapper.insertNamedBean_keyPropertyWithParamName(country2);
151         sqlSession.flushStatements();
152         sqlSession.clearCache();
153         assertNotNull(country1.getId());
154         assertNotNull(country2.getId());
155       } finally {
156         sqlSession.rollback();
157       }
158     }
159   }
160 
161   @Test
162   void shouldAssignKeysToList() {
163     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
164       try {
165         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
166         List<Country> countries = new ArrayList<>();
167         countries.add(new Country("China", "CN"));
168         countries.add(new Country("United Kiongdom", "GB"));
169         countries.add(new Country("United States of America", "US"));
170         mapper.insertList(countries);
171         for (Country country : countries) {
172           assertNotNull(country.getId());
173         }
174       } finally {
175         sqlSession.rollback();
176       }
177     }
178   }
179 
180   @Test
181   void shouldAssignKeysToNamedList() {
182     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
183       try {
184         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
185         List<Country> countries = new ArrayList<>();
186         countries.add(new Country("China", "CN"));
187         countries.add(new Country("United Kiongdom", "GB"));
188         countries.add(new Country("United States of America", "US"));
189         mapper.insertNamedList(countries);
190         for (Country country : countries) {
191           assertNotNull(country.getId());
192         }
193       } finally {
194         sqlSession.rollback();
195       }
196     }
197   }
198 
199   @Test
200   void shouldAssingKeysToCollection() {
201     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
202       try {
203         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
204         Set<Country> countries = new HashSet<>();
205         countries.add(new Country("China", "CN"));
206         countries.add(new Country("United Kiongdom", "GB"));
207         mapper.insertSet(countries);
208         for (Country country : countries) {
209           assertNotNull(country.getId());
210         }
211       } finally {
212         sqlSession.rollback();
213       }
214     }
215   }
216 
217   @Test
218   void shouldAssingKeysToNamedCollection() {
219     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
220       try {
221         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
222         Set<Country> countries = new HashSet<>();
223         countries.add(new Country("China", "CN"));
224         countries.add(new Country("United Kiongdom", "GB"));
225         mapper.insertNamedSet(countries);
226         for (Country country : countries) {
227           assertNotNull(country.getId());
228         }
229       } finally {
230         sqlSession.rollback();
231       }
232     }
233   }
234 
235   @Test
236   void shouldAssingKeysToArray() {
237     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
238       try {
239         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
240         Country[] countries = new Country[2];
241         countries[0] = new Country("China", "CN");
242         countries[1] = new Country("United Kiongdom", "GB");
243         mapper.insertArray(countries);
244         for (Country country : countries) {
245           assertNotNull(country.getId());
246         }
247       } finally {
248         sqlSession.rollback();
249       }
250     }
251   }
252 
253   @Test
254   void shouldAssingKeysToNamedArray() {
255     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
256       try {
257         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
258         Country[] countries = new Country[2];
259         countries[0] = new Country("China", "CN");
260         countries[1] = new Country("United Kiongdom", "GB");
261         mapper.insertNamedArray(countries);
262         for (Country country : countries) {
263           assertNotNull(country.getId());
264         }
265       } finally {
266         sqlSession.rollback();
267       }
268     }
269   }
270 
271   @Test
272   void shouldAssignKeyToBean_MultiParams() {
273     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
274       try {
275         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
276         Country country = new Country("China", "CN");
277         mapper.insertMultiParams(country, 1);
278         assertNotNull(country.getId());
279       } finally {
280         sqlSession.rollback();
281       }
282     }
283   }
284 
285   @Test
286   void shouldFailIfKeyPropertyIsInvalid_NoParamName() {
287     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
288       try {
289         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
290         Country country = new Country("China", "CN");
291         when(() -> mapper.insertMultiParams_keyPropertyWithoutParamName(country, 1));
292         then(caughtException()).isInstanceOf(PersistenceException.class)
293             .hasMessageContaining("Could not determine which parameter to assign generated keys to. "
294                 + "Note that when there are multiple parameters, 'keyProperty' must include the parameter name (e.g. 'param.id'). "
295                 + "Specified key properties are [id] and available parameters are [");
296       } finally {
297         sqlSession.rollback();
298       }
299     }
300   }
301 
302   @Test
303   void shouldFailIfKeyPropertyIsInvalid_WrongParamName() {
304     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
305       try {
306         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
307         Country country = new Country("China", "CN");
308         when(() -> mapper.insertMultiParams_keyPropertyWithWrongParamName(country, 1));
309         then(caughtException()).isInstanceOf(PersistenceException.class)
310             .hasMessageContaining("Could not find parameter 'bogus'. "
311                 + "Note that when there are multiple parameters, 'keyProperty' must include the parameter name (e.g. 'param.id'). "
312                 + "Specified key properties are [bogus.id] and available parameters are [");
313       } finally {
314         sqlSession.rollback();
315       }
316     }
317   }
318 
319   @Test
320   void shouldAssignKeysToNamedList_MultiParams() {
321     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
322       try {
323         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
324         List<Country> countries = new ArrayList<>();
325         countries.add(new Country("China", "CN"));
326         countries.add(new Country("United Kiongdom", "GB"));
327         mapper.insertList_MultiParams(countries, 1);
328         for (Country country : countries) {
329           assertNotNull(country.getId());
330         }
331       } finally {
332         sqlSession.rollback();
333       }
334     }
335   }
336 
337   @Test
338   void shouldAssignKeysToNamedCollection_MultiParams() {
339     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
340       try {
341         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
342         Set<Country> countries = new HashSet<>();
343         countries.add(new Country("China", "CN"));
344         countries.add(new Country("United Kiongdom", "GB"));
345         mapper.insertSet_MultiParams(countries, 1);
346         for (Country country : countries) {
347           assertNotNull(country.getId());
348         }
349       } finally {
350         sqlSession.rollback();
351       }
352     }
353   }
354 
355   @Test
356   void shouldAssignKeysToNamedArray_MultiParams() {
357     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
358       try {
359         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
360         Country[] countries = new Country[2];
361         countries[0] = new Country("China", "CN");
362         countries[1] = new Country("United Kiongdom", "GB");
363         mapper.insertArray_MultiParams(countries, 1);
364         for (Country country : countries) {
365           assertNotNull(country.getId());
366         }
367       } finally {
368         sqlSession.rollback();
369       }
370     }
371   }
372 
373   @Test
374   void shouldAssignMultipleGeneratedKeysToABean() {
375     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
376       try {
377         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
378         Planet planet = new Planet();
379         planet.setName("pluto");
380         mapper.insertPlanet(planet);
381         assertEquals("pluto-" + planet.getId(), planet.getCode());
382       } finally {
383         sqlSession.rollback();
384       }
385     }
386   }
387 
388   @Test
389   void shouldAssignMultipleGeneratedKeysToBeans() {
390     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
391       try {
392         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
393         Planet planet1 = new Planet();
394         planet1.setName("pluto");
395         Planet planet2 = new Planet();
396         planet2.setName("neptune");
397         List<Planet> planets = Arrays.asList(planet1, planet2);
398         mapper.insertPlanets(planets);
399         assertEquals("pluto-" + planet1.getId(), planet1.getCode());
400         assertEquals("neptune-" + planet2.getId(), planet2.getCode());
401       } finally {
402         sqlSession.rollback();
403       }
404     }
405   }
406 
407   @Test
408   void shouldAssignMultipleGeneratedKeysToABean_MultiParams() {
409     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
410       try {
411         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
412         Planet planet = new Planet();
413         planet.setName("pluto");
414         mapper.insertPlanet_MultiParams(planet, 1);
415         assertEquals("pluto-" + planet.getId(), planet.getCode());
416       } finally {
417         sqlSession.rollback();
418       }
419     }
420   }
421 
422   @Test
423   void shouldAssignMultipleGeneratedKeysToABean_MultiParams_batch() {
424     try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
425       try {
426         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
427         Planet planet1 = new Planet();
428         planet1.setName("pluto");
429         mapper.insertPlanet_MultiParams(planet1, 1);
430         Planet planet2 = new Planet();
431         planet2.setName("neptune");
432         mapper.insertPlanet_MultiParams(planet2, 1);
433         sqlSession.flushStatements();
434         sqlSession.clearCache();
435         assertEquals("pluto-" + planet1.getId(), planet1.getCode());
436         assertEquals("neptune-" + planet2.getId(), planet2.getCode());
437       } finally {
438         sqlSession.rollback();
439       }
440     }
441   }
442 
443   @Test
444   void shouldAssignMultipleGeneratedKeysToBeans_MultiParams() {
445     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
446       try {
447         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
448         Planet planet1 = new Planet();
449         planet1.setName("pluto");
450         Planet planet2 = new Planet();
451         planet2.setName("neptune");
452         List<Planet> planets = Arrays.asList(planet1, planet2);
453         mapper.insertPlanets_MultiParams(planets, 1);
454         assertEquals("pluto-" + planet1.getId(), planet1.getCode());
455         assertEquals("neptune-" + planet2.getId(), planet2.getCode());
456       } finally {
457         sqlSession.rollback();
458       }
459     }
460   }
461 
462   @Test
463   void assigningMultipleKeysToDifferentParams() {
464     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
465       try {
466         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
467         Planet planet = new Planet();
468         planet.setName("pluto");
469         Map<String, Object> map = new HashMap<>();
470         mapper.insertAssignKeysToTwoParams(planet, map);
471         assertNotNull(planet.getId());
472         assertNotNull(map.get("code"));
473       } finally {
474         sqlSession.rollback();
475       }
476     }
477   }
478 
479   @Test
480   void assigningMultipleKeysToDifferentParams_batch() {
481     try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
482       try {
483         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
484         Planet planet1 = new Planet();
485         planet1.setName("pluto");
486         Map<String, Object> map1 = new HashMap<>();
487         mapper.insertAssignKeysToTwoParams(planet1, map1);
488         Planet planet2 = new Planet();
489         planet2.setName("pluto");
490         Map<String, Object> map2 = new HashMap<>();
491         mapper.insertAssignKeysToTwoParams(planet2, map2);
492         sqlSession.flushStatements();
493         sqlSession.clearCache();
494         assertNotNull(planet1.getId());
495         assertNotNull(map1.get("code"));
496         assertNotNull(planet2.getId());
497         assertNotNull(map2.get("code"));
498       } finally {
499         sqlSession.rollback();
500       }
501     }
502   }
503 
504   @Test
505   void shouldErrorUndefineProperty() {
506     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
507       try {
508         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
509 
510         when(() -> mapper.insertUndefineKeyProperty(new Country("China", "CN")));
511         then(caughtException()).isInstanceOf(PersistenceException.class).hasMessageContaining(
512             "### Error updating database.  Cause: org.apache.ibatis.executor.ExecutorException: Error getting generated key or setting result to parameter object. Cause: org.apache.ibatis.executor.ExecutorException: No setter found for the keyProperty 'country_id' in 'org.apache.ibatis.submitted.keygen.Country'.");
513       } finally {
514         sqlSession.rollback();
515       }
516     }
517   }
518 
519   @Test
520   void shouldFailIfTooManyGeneratedKeys() {
521     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
522       try {
523         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
524         when(() -> mapper.tooManyGeneratedKeys(new Country()));
525         then(caughtException()).isInstanceOf(PersistenceException.class)
526             .hasMessageContaining("Too many keys are generated. There are only 1 target objects.");
527       } finally {
528         sqlSession.rollback();
529       }
530     }
531   }
532 
533   @Test
534   void shouldFailIfTooManyGeneratedKeys_ParamMap() {
535     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
536       try {
537         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
538         when(() -> mapper.tooManyGeneratedKeysParamMap(new Country(), 1));
539         then(caughtException()).isInstanceOf(PersistenceException.class)
540             .hasMessageContaining("Too many keys are generated. There are only 1 target objects.");
541       } finally {
542         sqlSession.rollback();
543       }
544     }
545   }
546 
547   @Test
548   void shouldFailIfTooManyGeneratedKeys_Batch() {
549     try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
550       try {
551         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
552         mapper.tooManyGeneratedKeysParamMap(new Country(), 1);
553         mapper.tooManyGeneratedKeysParamMap(new Country(), 1);
554         when(sqlSession::flushStatements);
555         then(caughtException()).isInstanceOf(PersistenceException.class)
556             .hasMessageContaining("Too many keys are generated. There are only 2 target objects.");
557       } finally {
558         sqlSession.rollback();
559       }
560     }
561   }
562 
563   @Test
564   void shouldAssignKeysToListWithoutInvokingEqualsNorHashCode() {
565     // gh-1719
566     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
567       try {
568         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
569         List<NpeCountry> countries = new ArrayList<>();
570         countries.add(new NpeCountry("China", "CN"));
571         countries.add(new NpeCountry("United Kiongdom", "GB"));
572         countries.add(new NpeCountry("United States of America", "US"));
573         mapper.insertWeirdCountries(countries);
574         for (NpeCountry country : countries) {
575           assertNotNull(country.getId());
576         }
577       } finally {
578         sqlSession.rollback();
579       }
580     }
581   }
582 
583   @Test
584   void shouldAssignKeyToAParamWithTrickyName() {
585     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
586       try {
587         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
588         Country country = new Country("China", "CN");
589         mapper.singleParamWithATrickyName(country);
590         assertNotNull(country.getId());
591       } finally {
592         sqlSession.rollback();
593       }
594     }
595   }
596 
597   @Test
598   void shouldAssingKeysToAMap() {
599     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
600       try {
601         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
602         Map<String, Object> map = new HashMap<>();
603         map.put("countrycode", "CN");
604         map.put("countryname", "China");
605         mapper.insertMap(map);
606         assertNotNull(map.get("id"));
607       } finally {
608         sqlSession.rollback();
609       }
610     }
611   }
612 }