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