View Javadoc
1   /*
2    *    Copyright 2009-2021 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    *       http://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.type;
17  
18  import static org.junit.jupiter.api.Assertions.*;
19  
20  import java.net.URI;
21  import java.sql.CallableStatement;
22  import java.sql.PreparedStatement;
23  import java.sql.ResultSet;
24  import java.sql.SQLException;
25  import java.util.Date;
26  import java.util.List;
27  import java.util.concurrent.ExecutorService;
28  import java.util.concurrent.Executors;
29  import java.util.concurrent.Future;
30  import java.util.stream.Collectors;
31  import java.util.stream.IntStream;
32  
33  import org.apache.ibatis.domain.misc.RichType;
34  import org.junit.jupiter.api.BeforeEach;
35  import org.junit.jupiter.api.Test;
36  
37  class TypeHandlerRegistryTest {
38  
39    private TypeHandlerRegistry typeHandlerRegistry;
40  
41    @BeforeEach
42    void setup() {
43      typeHandlerRegistry = new TypeHandlerRegistry();
44    }
45  
46    @Test
47    void shouldRegisterAndRetrieveTypeHandler() {
48      TypeHandler<String> stringTypeHandler = typeHandlerRegistry.getTypeHandler(String.class);
49      typeHandlerRegistry.register(String.class, JdbcType.LONGVARCHAR, stringTypeHandler);
50      assertEquals(stringTypeHandler, typeHandlerRegistry.getTypeHandler(String.class, JdbcType.LONGVARCHAR));
51  
52      assertTrue(typeHandlerRegistry.hasTypeHandler(String.class));
53      assertFalse(typeHandlerRegistry.hasTypeHandler(RichType.class));
54      assertTrue(typeHandlerRegistry.hasTypeHandler(String.class, JdbcType.LONGVARCHAR));
55      assertTrue(typeHandlerRegistry.hasTypeHandler(String.class, JdbcType.INTEGER));
56      assertTrue(typeHandlerRegistry.getUnknownTypeHandler() instanceof UnknownTypeHandler);
57    }
58  
59    @Test
60    void shouldRegisterAndRetrieveComplexTypeHandler() {
61      TypeHandler<List<URI>> fakeHandler = new TypeHandler<List<URI>>() {
62  
63        @Override
64        public void setParameter(PreparedStatement ps, int i, List<URI> parameter, JdbcType jdbcType) {
65          // do nothing, fake method
66        }
67  
68        @Override
69        public List<URI> getResult(CallableStatement cs, int columnIndex) {
70          // do nothing, fake method
71          return null;
72        }
73  
74        @Override
75        public List<URI> getResult(ResultSet rs, int columnIndex) {
76          // do nothing, fake method
77          return null;
78        }
79  
80        @Override
81        public List<URI> getResult(ResultSet rs, String columnName) {
82          // do nothing, fake method
83          return null;
84        }
85  
86      };
87  
88      TypeReference<List<URI>> type = new TypeReference<List<URI>>(){};
89  
90      typeHandlerRegistry.register(type, fakeHandler);
91      assertSame(fakeHandler, typeHandlerRegistry.getTypeHandler(type));
92    }
93  
94    @Test
95    void shouldAutoRegisterAndRetrieveComplexTypeHandler() {
96      TypeHandler<List<URI>> fakeHandler = new BaseTypeHandler<List<URI>>() {
97  
98        @Override
99        public void setNonNullParameter(PreparedStatement ps, int i, List<URI> parameter, JdbcType jdbcType) {
100         // do nothing, fake method
101       }
102 
103       @Override
104       public List<URI> getNullableResult(ResultSet rs, String columnName) {
105         // do nothing, fake method
106         return null;
107       }
108 
109       @Override
110       public List<URI> getNullableResult(ResultSet rs, int columnIndex) {
111         // do nothing, fake method
112         return null;
113       }
114 
115       @Override
116       public List<URI> getNullableResult(CallableStatement cs, int columnIndex) {
117         // do nothing, fake method
118         return null;
119       }
120 
121     };
122 
123     typeHandlerRegistry.register(fakeHandler);
124 
125     assertSame(fakeHandler, typeHandlerRegistry.getTypeHandler(new TypeReference<List<URI>>(){}));
126   }
127 
128   @Test
129   void shouldBindHandlersToWrappersAndPrimitivesIndividually() {
130     typeHandlerRegistry.register(Integer.class, DateTypeHandler.class);
131     assertSame(IntegerTypeHandler.class, typeHandlerRegistry.getTypeHandler(int.class).getClass());
132     typeHandlerRegistry.register(Integer.class, IntegerTypeHandler.class);
133     typeHandlerRegistry.register(int.class, DateTypeHandler.class);
134     assertSame(IntegerTypeHandler.class, typeHandlerRegistry.getTypeHandler(Integer.class).getClass());
135     typeHandlerRegistry.register(Integer.class, IntegerTypeHandler.class);
136   }
137 
138   @Test
139   void shouldReturnHandlerForSuperclassIfRegistered() {
140     class MyDate extends Date {
141       private static final long serialVersionUID = 1L;
142     }
143     assertEquals(DateTypeHandler.class, typeHandlerRegistry.getTypeHandler(MyDate.class).getClass());
144   }
145 
146   @Test
147   void shouldReturnHandlerForSuperSuperclassIfRegistered() {
148     class MyDate1 extends Date {
149       private static final long serialVersionUID = 1L;
150     }
151     class MyDate2 extends MyDate1 {
152       private static final long serialVersionUID = 1L;
153     }
154     assertEquals(DateTypeHandler.class, typeHandlerRegistry.getTypeHandler(MyDate2.class).getClass());
155   }
156 
157   interface SomeInterface {
158   }
159 
160   interface ExtendingSomeInterface extends SomeInterface {
161   }
162 
163   interface NoTypeHandlerInterface {
164   }
165 
166   enum SomeEnum implements SomeInterface {
167   }
168 
169   enum ExtendingSomeEnum implements ExtendingSomeInterface {
170   }
171 
172   enum ImplementingMultiInterfaceSomeEnum implements NoTypeHandlerInterface, ExtendingSomeInterface {
173   }
174 
175   enum NoTypeHandlerInterfaceEnum implements NoTypeHandlerInterface {
176   }
177 
178   class SomeClass implements SomeInterface {
179   }
180 
181   @MappedTypes(SomeInterface.class)
182   public static class SomeInterfaceTypeHandler<E extends Enum<E> & SomeInterface> extends BaseTypeHandler<E> {
183     @Override
184     public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
185     }
186 
187     @Override
188     public E getNullableResult(ResultSet rs, String columnName) throws SQLException {
189       return null;
190     }
191 
192     @Override
193     public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
194       return null;
195     }
196 
197     @Override
198     public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
199       return null;
200     }
201   }
202 
203   @Test
204   void demoTypeHandlerForSuperInterface() {
205     typeHandlerRegistry.register(SomeInterfaceTypeHandler.class);
206     assertNull(typeHandlerRegistry.getTypeHandler(SomeClass.class), "Registering interface works only for enums.");
207     assertSame(EnumTypeHandler.class, typeHandlerRegistry.getTypeHandler(NoTypeHandlerInterfaceEnum.class).getClass(),
208         "When type handler for interface is not exist, apply default enum type handler.");
209     assertSame(SomeInterfaceTypeHandler.class, typeHandlerRegistry.getTypeHandler(SomeEnum.class).getClass());
210     assertSame(SomeInterfaceTypeHandler.class, typeHandlerRegistry.getTypeHandler(ExtendingSomeEnum.class).getClass());
211     assertSame(SomeInterfaceTypeHandler.class,
212         typeHandlerRegistry.getTypeHandler(ImplementingMultiInterfaceSomeEnum.class).getClass());
213   }
214 
215   @Test
216   void shouldRegisterReplaceNullMap() {
217     class Address {
218     }
219     assertFalse(typeHandlerRegistry.hasTypeHandler(Address.class));
220     typeHandlerRegistry.register(Address.class, StringTypeHandler.class);
221     assertTrue(typeHandlerRegistry.hasTypeHandler(Address.class));
222   }
223 
224   enum TestEnum {
225     ONE,
226     TWO
227   }
228 
229   @Test
230   void shouldAutoRegisterEnumTypeInMultiThreadEnvironment() throws Exception {
231     // gh-1820
232     ExecutorService executorService = Executors.newCachedThreadPool();
233     try {
234       for (int iteration = 0; iteration < 2000; iteration++) {
235         TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
236         List<Future<Boolean>> taskResults = IntStream.range(0, 2)
237             .mapToObj(taskIndex -> executorService.submit(() -> {
238               return typeHandlerRegistry.hasTypeHandler(TestEnum.class, JdbcType.VARCHAR);
239             })).collect(Collectors.toList());
240         for (int i = 0; i < taskResults.size(); i++) {
241           Future<Boolean> future = taskResults.get(i);
242           assertTrue(future.get(), "false is returned at round " + iteration);
243         }
244       }
245     } finally {
246       executorService.shutdownNow();
247     }
248   }
249 }