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.*;
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
66 }
67
68 @Override
69 public List<URI> getResult(CallableStatement cs, int columnIndex) {
70
71 return null;
72 }
73
74 @Override
75 public List<URI> getResult(ResultSet rs, int columnIndex) {
76
77 return null;
78 }
79
80 @Override
81 public List<URI> getResult(ResultSet rs, String columnName) {
82
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
101 }
102
103 @Override
104 public List<URI> getNullableResult(ResultSet rs, String columnName) {
105
106 return null;
107 }
108
109 @Override
110 public List<URI> getNullableResult(ResultSet rs, int columnIndex) {
111
112 return null;
113 }
114
115 @Override
116 public List<URI> getNullableResult(CallableStatement cs, int columnIndex) {
117
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
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 }