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.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).hasMessageContaining(
293             """
294                 Could not determine which parameter to assign generated keys to. \
295                 Note that when there are multiple parameters, 'keyProperty' must include the parameter name (e.g. 'param.id'). \
296                 Specified key properties are [id] and available parameters are [""");
297       } finally {
298         sqlSession.rollback();
299       }
300     }
301   }
302 
303   @Test
304   void shouldFailIfKeyPropertyIsInvalid_WrongParamName() {
305     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
306       try {
307         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
308         Country country = new Country("China", "CN");
309         when(() -> mapper.insertMultiParams_keyPropertyWithWrongParamName(country, 1));
310         then(caughtException()).isInstanceOf(PersistenceException.class).hasMessageContaining(
311             """
312                 Could not find parameter 'bogus'. \
313                 Note that when there are multiple parameters, 'keyProperty' must include the parameter name (e.g. 'param.id'). \
314                 Specified key properties are [bogus.id] and available parameters are [""");
315       } finally {
316         sqlSession.rollback();
317       }
318     }
319   }
320 
321   @Test
322   void shouldAssignKeysToNamedList_MultiParams() {
323     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
324       try {
325         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
326         List<Country> countries = new ArrayList<>();
327         countries.add(new Country("China", "CN"));
328         countries.add(new Country("United Kiongdom", "GB"));
329         mapper.insertList_MultiParams(countries, 1);
330         for (Country country : countries) {
331           assertNotNull(country.getId());
332         }
333       } finally {
334         sqlSession.rollback();
335       }
336     }
337   }
338 
339   @Test
340   void shouldAssignKeysToNamedCollection_MultiParams() {
341     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
342       try {
343         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
344         Set<Country> countries = new HashSet<>();
345         countries.add(new Country("China", "CN"));
346         countries.add(new Country("United Kiongdom", "GB"));
347         mapper.insertSet_MultiParams(countries, 1);
348         for (Country country : countries) {
349           assertNotNull(country.getId());
350         }
351       } finally {
352         sqlSession.rollback();
353       }
354     }
355   }
356 
357   @Test
358   void shouldAssignKeysToNamedArray_MultiParams() {
359     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
360       try {
361         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
362         Country[] countries = new Country[2];
363         countries[0] = new Country("China", "CN");
364         countries[1] = new Country("United Kiongdom", "GB");
365         mapper.insertArray_MultiParams(countries, 1);
366         for (Country country : countries) {
367           assertNotNull(country.getId());
368         }
369       } finally {
370         sqlSession.rollback();
371       }
372     }
373   }
374 
375   @Test
376   void shouldAssignMultipleGeneratedKeysToABean() {
377     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
378       try {
379         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
380         Planet planet = new Planet();
381         planet.setName("pluto");
382         mapper.insertPlanet(planet);
383         assertEquals("pluto-" + planet.getId(), planet.getCode());
384       } finally {
385         sqlSession.rollback();
386       }
387     }
388   }
389 
390   @Test
391   void shouldAssignMultipleGeneratedKeysToBeans() {
392     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
393       try {
394         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
395         Planet planet1 = new Planet();
396         planet1.setName("pluto");
397         Planet planet2 = new Planet();
398         planet2.setName("neptune");
399         List<Planet> planets = Arrays.asList(planet1, planet2);
400         mapper.insertPlanets(planets);
401         assertEquals("pluto-" + planet1.getId(), planet1.getCode());
402         assertEquals("neptune-" + planet2.getId(), planet2.getCode());
403       } finally {
404         sqlSession.rollback();
405       }
406     }
407   }
408 
409   @Test
410   void shouldAssignMultipleGeneratedKeysToABean_MultiParams() {
411     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
412       try {
413         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
414         Planet planet = new Planet();
415         planet.setName("pluto");
416         mapper.insertPlanet_MultiParams(planet, 1);
417         assertEquals("pluto-" + planet.getId(), planet.getCode());
418       } finally {
419         sqlSession.rollback();
420       }
421     }
422   }
423 
424   @Test
425   void shouldAssignMultipleGeneratedKeysToABean_MultiParams_batch() {
426     try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
427       try {
428         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
429         Planet planet1 = new Planet();
430         planet1.setName("pluto");
431         mapper.insertPlanet_MultiParams(planet1, 1);
432         Planet planet2 = new Planet();
433         planet2.setName("neptune");
434         mapper.insertPlanet_MultiParams(planet2, 1);
435         sqlSession.flushStatements();
436         sqlSession.clearCache();
437         assertEquals("pluto-" + planet1.getId(), planet1.getCode());
438         assertEquals("neptune-" + planet2.getId(), planet2.getCode());
439       } finally {
440         sqlSession.rollback();
441       }
442     }
443   }
444 
445   @Test
446   void shouldAssignMultipleGeneratedKeysToBeans_MultiParams() {
447     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
448       try {
449         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
450         Planet planet1 = new Planet();
451         planet1.setName("pluto");
452         Planet planet2 = new Planet();
453         planet2.setName("neptune");
454         List<Planet> planets = Arrays.asList(planet1, planet2);
455         mapper.insertPlanets_MultiParams(planets, 1);
456         assertEquals("pluto-" + planet1.getId(), planet1.getCode());
457         assertEquals("neptune-" + planet2.getId(), planet2.getCode());
458       } finally {
459         sqlSession.rollback();
460       }
461     }
462   }
463 
464   @Test
465   void assigningMultipleKeysToDifferentParams() {
466     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
467       try {
468         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
469         Planet planet = new Planet();
470         planet.setName("pluto");
471         Map<String, Object> map = new HashMap<>();
472         mapper.insertAssignKeysToTwoParams(planet, map);
473         assertNotNull(planet.getId());
474         assertNotNull(map.get("code"));
475       } finally {
476         sqlSession.rollback();
477       }
478     }
479   }
480 
481   @Test
482   void assigningMultipleKeysToDifferentParams_batch() {
483     try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
484       try {
485         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
486         Planet planet1 = new Planet();
487         planet1.setName("pluto");
488         Map<String, Object> map1 = new HashMap<>();
489         mapper.insertAssignKeysToTwoParams(planet1, map1);
490         Planet planet2 = new Planet();
491         planet2.setName("pluto");
492         Map<String, Object> map2 = new HashMap<>();
493         mapper.insertAssignKeysToTwoParams(planet2, map2);
494         sqlSession.flushStatements();
495         sqlSession.clearCache();
496         assertNotNull(planet1.getId());
497         assertNotNull(map1.get("code"));
498         assertNotNull(planet2.getId());
499         assertNotNull(map2.get("code"));
500       } finally {
501         sqlSession.rollback();
502       }
503     }
504   }
505 
506   @Test
507   void shouldErrorUndefineProperty() {
508     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
509       try {
510         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
511 
512         when(() -> mapper.insertUndefineKeyProperty(new Country("China", "CN")));
513         then(caughtException()).isInstanceOf(PersistenceException.class).hasMessageContaining(
514             "### 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'.");
515       } finally {
516         sqlSession.rollback();
517       }
518     }
519   }
520 
521   @Test
522   void shouldFailIfTooManyGeneratedKeys() {
523     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
524       try {
525         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
526         when(() -> mapper.tooManyGeneratedKeys(new Country()));
527         then(caughtException()).isInstanceOf(PersistenceException.class)
528             .hasMessageContaining("Too many keys are generated. There are only 1 target objects.");
529       } finally {
530         sqlSession.rollback();
531       }
532     }
533   }
534 
535   @Test
536   void shouldFailIfTooManyGeneratedKeys_ParamMap() {
537     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
538       try {
539         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
540         when(() -> mapper.tooManyGeneratedKeysParamMap(new Country(), 1));
541         then(caughtException()).isInstanceOf(PersistenceException.class)
542             .hasMessageContaining("Too many keys are generated. There are only 1 target objects.");
543       } finally {
544         sqlSession.rollback();
545       }
546     }
547   }
548 
549   @Test
550   void shouldFailIfTooManyGeneratedKeys_Batch() {
551     try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
552       try {
553         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
554         mapper.tooManyGeneratedKeysParamMap(new Country(), 1);
555         mapper.tooManyGeneratedKeysParamMap(new Country(), 1);
556         when(sqlSession::flushStatements);
557         then(caughtException()).isInstanceOf(PersistenceException.class)
558             .hasMessageContaining("Too many keys are generated. There are only 2 target objects.");
559       } finally {
560         sqlSession.rollback();
561       }
562     }
563   }
564 
565   @Test
566   void shouldAssignKeysToListWithoutInvokingEqualsNorHashCode() {
567     // gh-1719
568     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
569       try {
570         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
571         List<NpeCountry> countries = new ArrayList<>();
572         countries.add(new NpeCountry("China", "CN"));
573         countries.add(new NpeCountry("United Kiongdom", "GB"));
574         countries.add(new NpeCountry("United States of America", "US"));
575         mapper.insertWeirdCountries(countries);
576         for (NpeCountry country : countries) {
577           assertNotNull(country.getId());
578         }
579       } finally {
580         sqlSession.rollback();
581       }
582     }
583   }
584 
585   @Test
586   void shouldAssignKeyToAParamWithTrickyName() {
587     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
588       try {
589         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
590         Country country = new Country("China", "CN");
591         mapper.singleParamWithATrickyName(country);
592         assertNotNull(country.getId());
593       } finally {
594         sqlSession.rollback();
595       }
596     }
597   }
598 
599   @Test
600   void shouldAssingKeysToAMap() {
601     try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
602       try {
603         CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
604         Map<String, Object> map = new HashMap<>();
605         map.put("countrycode", "CN");
606         map.put("countryname", "China");
607         mapper.insertMap(map);
608         assertNotNull(map.get("id"));
609       } finally {
610         sqlSession.rollback();
611       }
612     }
613   }
614 }