【Mybatis 系列10-结合源码解析mybatis 执行流程】
【Mybatis 系列9-强大的动态sql 语句】
【Mybatis 系列8-结合源码解析select、resultMap的用法】
【Mybatis 系列7-结合源码解析核心CRUD配置及用法】
【Mybatis 系列6-结合源码解析节点配置objectFactory、databaseIdProvider、plugins、mappers】
【Mybatis 系列5-结合源码解析TypeHandler】
【Mybatis 系列4-结合源码解析节点typeAliases】
【Mybatis 系列3-结合源码解析properties节点和environments节点】
【Mybatis 系列2-配置文件】
【Mybatis 系列1-环境搭建】
上篇系列4中 为大家介绍了mybatis中别名的使用,以及其源码。本篇将为大家介绍TypeHandler, 并简单分析其源码。
Mybatis中的TypeHandler是什么?
无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换成 Java 类型。Mybatis默认为我们实现了许多TypeHandler, 当我们没有配置指定TypeHandler时,Mybatis会根据参数或者返回结果的不同,默认为我们选择合适的TypeHandler处理。
那么,Mybatis为我们实现了哪些TypeHandler呢? 我们怎么自定义实现一个TypeHandler ?
这些都会在接下来的mybatis的源码中看到。
在看源码之前,先看看怎么配置吧?
配置TypeHandler:
1
2
3
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 ......
23
24
上面简单介绍了一下TypeHandler, 下面就看看mybatis中TypeHandler的源码了。
=我是源码分割线==
老规矩,先从对xml的解析讲起:
1 /**
2 * 解析typeHandlers节点
3 */
4 private void typeHandlerElement(XNode parent) throws Exception {
5 if (parent != null ) {
6 for (XNode child : parent.getChildren()) {
7 // 子节点为package时,获取其name属性的值,然后自动扫描package下的自定义typeHandler
8 if ("package".equals(child.getName())) {
9 String typeHandlerPackage = child.getStringAttribute("name");
10 typeHandlerRegistry.register(typeHandlerPackage);
11 } else {
12 // 子节点为typeHandler时, 可以指定javaType属性, 也可以指定jdbcType, 也可两者都指定
13 // javaType 是指定java类型
14 // jdbcType 是指定jdbc类型(数据库类型: 如varchar)
15 String javaTypeName = child.getStringAttribute("javaType");
16 String jdbcTypeName = child.getStringAttribute("jdbcType");
17 // handler就是我们配置的typeHandler
18 String handlerTypeName = child.getStringAttribute("handler");
19 // resolveClass方法就是我们上篇文章所讲的TypeAliasRegistry里面处理别名的方法
20 Class> javaTypeClass = resolveClass(javaTypeName);
21 // JdbcType是一个枚举类型,resolveJdbcType方法是在获取枚举类型的值
22 JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
23 Class> typeHandlerClass = resolveClass(handlerTypeName);
24 // 注册typeHandler, typeHandler通过TypeHandlerRegistry这个类管理
25 if (javaTypeClass != null ) {
26 if (jdbcType == null ) {
27 typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
28 } else {
29 typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
30 }
31 } else {
32 typeHandlerRegistry.register(typeHandlerClass);
33 }
34 }
35 }
36 }
37 }
接下来看看TypeHandler的管理注册类:
TypeHandlerRegistry:
1 /**
2 * typeHandler注册管理类
3 */
4 public final class TypeHandlerRegistry {
5
6 // 源码一上来,二话不说,几个大大的HashMap就出现,这不又跟上次讲的typeAliases的注册类似么
7
8 // 基本数据类型与其包装类
9 private static final Map, Class>> reversePrimitiveMap = new HashMap, Class>>() {
10 private static final long serialVersionUID = 1L;
11 {
12 put(Byte.class , byte .class );
13 put(Short.class , short .class );
14 put(Integer.class , int .class );
15 put(Long.class , long .class );
16 put(Float.class , float .class );
17 put(Double.class , double .class );
18 put(Boolean.class , boolean .class );
19 put(Character.class , char .class );
20 }
21 };
22
23 // 这几个MAP不用说就知道存的是什么东西吧,命名的好处
24 private final Map> JDBC_TYPE_HANDLER_MAP = new EnumMap>(JdbcType.class );
25 private final Map>> TYPE_HANDLER_MAP = new HashMap>>();
26 private final TypeHandler UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this );
27 private final Map, TypeHandler>> ALL_TYPE_HANDLERS_MAP = new HashMap, TypeHandler>>();
28
29 // 就像上篇文章讲的typeAliases一样,mybatis也默认给我们注册了不少的typeHandler
30 // 具体如下
31 public TypeHandlerRegistry() {
32 register(Boolean.class , new BooleanTypeHandler());
33 register(boolean .class , new BooleanTypeHandler());
34 register(JdbcType.BOOLEAN, new BooleanTypeHandler());
35 register(JdbcType.BIT, new BooleanTypeHandler());
36
37 register(Byte.class , new ByteTypeHandler());
38 register(byte .class , new ByteTypeHandler());
39 register(JdbcType.TINYINT, new ByteTypeHandler());
40
41 register(Short.class , new ShortTypeHandler());
42 register(short .class , new ShortTypeHandler());
43 register(JdbcType.SMALLINT, new ShortTypeHandler());
44
45 register(Integer.class , new IntegerTypeHandler());
46 register(int .class , new IntegerTypeHandler());
47 register(JdbcType.INTEGER, new IntegerTypeHandler());
48
49 register(Long.class , new LongTypeHandler());
50 register(long .class , new LongTypeHandler());
51
52 register(Float.class , new FloatTypeHandler());
53 register(float .class , new FloatTypeHandler());
54 register(JdbcType.FLOAT, new FloatTypeHandler());
55
56 register(Double.class , new DoubleTypeHandler());
57 register(double .class , new DoubleTypeHandler());
58 register(JdbcType.DOUBLE, new DoubleTypeHandler());
59
60 register(String.class , new StringTypeHandler());
61 register(String.class , JdbcType.CHAR, new StringTypeHandler());
62 register(String.class , JdbcType.CLOB, new ClobTypeHandler());
63 register(String.class , JdbcType.VARCHAR, new StringTypeHandler());
64 register(String.class , JdbcType.LONGVARCHAR, new ClobTypeHandler());
65 register(String.class , JdbcType.NVARCHAR, new NStringTypeHandler());
66 register(String.class , JdbcType.NCHAR, new NStringTypeHandler());
67 register(String.class , JdbcType.NCLOB, new NClobTypeHandler());
68 register(JdbcType.CHAR, new StringTypeHandler());
69 register(JdbcType.VARCHAR, new StringTypeHandler());
70 register(JdbcType.CLOB, new ClobTypeHandler());
71 register(JdbcType.LONGVARCHAR, new ClobTypeHandler());
72 register(JdbcType.NVARCHAR, new NStringTypeHandler());
73 register(JdbcType.NCHAR, new NStringTypeHandler());
74 register(JdbcType.NCLOB, new NClobTypeHandler());
75
76 register(Object.class , JdbcType.ARRAY, new ArrayTypeHandler());
77 register(JdbcType.ARRAY, new ArrayTypeHandler());
78
79 register(BigInteger.class , new BigIntegerTypeHandler());
80 register(JdbcType.BIGINT, new LongTypeHandler());
81
82 register(BigDecimal.class , new BigDecimalTypeHandler());
83 register(JdbcType.REAL, new BigDecimalTypeHandler());
84 register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
85 register(JdbcType.NUMERIC, new BigDecimalTypeHandler());
86
87 register(Byte[].class , new ByteObjectArrayTypeHandler());
88 register(Byte[].class , JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
89 register(Byte[].class , JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
90 register(byte [].class , new ByteArrayTypeHandler());
91 register(byte [].class , JdbcType.BLOB, new BlobTypeHandler());
92 register(byte [].class , JdbcType.LONGVARBINARY, new BlobTypeHandler());
93 register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
94 register(JdbcType.BLOB, new BlobTypeHandler());
95
96 register(Object.class , UNKNOWN_TYPE_HANDLER);
97 register(Object.class , JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
98 register(JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
99
100 register(Date.class , new DateTypeHandler());
101 register(Date.class , JdbcType.DATE, new DateOnlyTypeHandler());
102 register(Date.class , JdbcType.TIME, new TimeOnlyTypeHandler());
103 register(JdbcType.TIMESTAMP, new DateTypeHandler());
104 register(JdbcType.DATE, new DateOnlyTypeHandler());
105 register(JdbcType.TIME, new TimeOnlyTypeHandler());
106
107 register(java.sql.Date.class , new SqlDateTypeHandler());
108 register(java.sql.Time.class , new SqlTimeTypeHandler());
109 register(java.sql.Timestamp.class , new SqlTimestampTypeHandler());
110
111 // issue #273
112 register(Character.class , new CharacterTypeHandler());
113 register(char .class , new CharacterTypeHandler());
114 }
115
116 public boolean hasTypeHandler(Class> javaType) {
117 return hasTypeHandler(javaType, null );
118 }
119
120 public boolean hasTypeHandler(TypeReference> javaTypeReference) {
121 return hasTypeHandler(javaTypeReference, null );
122 }
123
124 public boolean hasTypeHandler(Class> javaType, JdbcType jdbcType) {
125 return javaType != null && getTypeHandler((Type) javaType, jdbcType) != null ;
126 }
127
128 public boolean hasTypeHandler(TypeReference> javaTypeReference, JdbcType jdbcType) {
129 return javaTypeReference != null && getTypeHandler(javaTypeReference, jdbcType) != null ;
130 }
131
132 public TypeHandler> getMappingTypeHandler(Class extends TypeHandler>> handlerType) {
133 return ALL_TYPE_HANDLERS_MAP.get(handlerType);
134 }
135
136 public TypeHandler getTypeHandler(Class type) {
137 return getTypeHandler((Type) type, null );
138 }
139
140 public TypeHandler getTypeHandler(TypeReference javaTypeReference) {
141 return getTypeHandler(javaTypeReference, null );
142 }
143
144 public TypeHandler> getTypeHandler(JdbcType jdbcType) {
145 return JDBC_TYPE_HANDLER_MAP.get(jdbcType);
146 }
147
148 public TypeHandler getTypeHandler(Class type, JdbcType jdbcType) {
149 return getTypeHandler((Type) type, jdbcType);
150 }
151
152 public TypeHandler getTypeHandler(TypeReference javaTypeReference, JdbcType jdbcType) {
153 return getTypeHandler(javaTypeReference.getRawType(), jdbcType);
154 }
155
156 private TypeHandler getTypeHandler(Type type, JdbcType jdbcType) {
157 Map> jdbcHandlerMap = TYPE_HANDLER_MAP.get(type);
158 TypeHandler> handler = null ;
159 if (jdbcHandlerMap != null ) {
160 handler = jdbcHandlerMap.get(jdbcType);
161 if (handler == null ) {
162 handler = jdbcHandlerMap.get(null );
163 }
164 }
165 if (handler == null && type != null && type instanceof Class && Enum.class .isAssignableFrom((Class>) type)) {
166 handler = new EnumTypeHandler((Class>) type);
167 }
168 @SuppressWarnings("unchecked")
169 // type drives generics here
170 TypeHandler returned = (TypeHandler) handler;
171 return returned;
172 }
173
174 public TypeHandler getUnknownTypeHandler() {
175 return UNKNOWN_TYPE_HANDLER;
176 }
177
178 public void register(JdbcType jdbcType, TypeHandler> handler) {
179 JDBC_TYPE_HANDLER_MAP.put(jdbcType, handler);
180 }
181
182 //
183 // REGISTER INSTANCE
184 //
185
186 /**
187 * 只配置了typeHandler, 没有配置jdbcType 或者javaType
188 */
189 @SuppressWarnings("unchecked")
190 public void register(TypeHandler typeHandler) {
191 boolean mappedTypeFound = false ;
192 // 在自定义typeHandler的时候,可以加上注解MappedTypes 去指定关联的javaType
193 // 因此,此处需要扫描MappedTypes注解
194 MappedTypes mappedTypes = typeHandler.getClass().getAnnotation(MappedTypes.class );
195 if (mappedTypes != null ) {
196 for (Class> handledType : mappedTypes.value()) {
197 register(handledType, typeHandler);
198 mappedTypeFound = true ;
199 }
200 }
201 // @since 3.1.0 - try to auto-discover the mapped type
202 if (!mappedTypeFound && typeHandler instanceof TypeReference) {
203 try {
204 TypeReference typeReference = (TypeReference) typeHandler;
205 register(typeReference.getRawType(), typeHandler);
206 mappedTypeFound = true ;
207 } catch (Throwable t) {
208 // maybe users define the TypeReference with a different type and are not assignable, so just ignore it
209 }
210 }
211 if (!mappedTypeFound) {
212 register((Class) null , typeHandler);
213 }
214 }
215
216 /**
217 * 配置了typeHandlerhe和javaType
218 */
219 public void register(Class javaType, TypeHandler extends T> typeHandler) {
220 register((Type) javaType, typeHandler);
221 }
222
223 private void register(Type javaType, TypeHandler extends T> typeHandler) {
224 // 扫描注解MappedJdbcTypes
225 MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class );
226 if (mappedJdbcTypes != null ) {
227 for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
228 register(javaType, handledJdbcType, typeHandler);
229 }
230 if (mappedJdbcTypes.includeNullJdbcType()) {
231 register(javaType, null , typeHandler);
232 }
233 } else {
234 register(javaType, null , typeHandler);
235 }
236 }
237
238 public void register(TypeReference javaTypeReference, TypeHandler extends T> handler) {
239 register(javaTypeReference.getRawType(), handler);
240 }
241
242 /**
243 * typeHandlerhe、javaType、jdbcType都配置了
244 */
245 public void register(Class type, JdbcType jdbcType, TypeHandler extends T> handler) {
246 register((Type) type, jdbcType, handler);
247 }
248
249 /**
250 * 注册typeHandler的核心方法
251 * 就是向Map新增数据而已
252 */
253 private void register(Type javaType, JdbcType jdbcType, TypeHandler> handler) {
254 if (javaType != null ) {
255 Map> map = TYPE_HANDLER_MAP.get(javaType);
256 if (map == null ) {
257 map = new HashMap>();
258 TYPE_HANDLER_MAP.put(javaType, map);
259 }
260 map.put(jdbcType, handler);
261 if (reversePrimitiveMap.containsKey(javaType)) {
262 register(reversePrimitiveMap.get(javaType), jdbcType, handler);
263 }
264 }
265 ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
266 }
267
268 //
269 // REGISTER CLASS
270 //
271
272 // Only handler type
273
274 public void register(Class> typeHandlerClass) {
275 boolean mappedTypeFound = false ;
276 MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class );
277 if (mappedTypes != null ) {
278 for (Class> javaTypeClass : mappedTypes.value()) {
279 register(javaTypeClass, typeHandlerClass);
280 mappedTypeFound = true ;
281 }
282 }
283 if (!mappedTypeFound) {
284 register(getInstance(null , typeHandlerClass));
285 }
286 }
287
288 // java type + handler type
289
290 public void register(Class> javaTypeClass, Class> typeHandlerClass) {
291 register(javaTypeClass, getInstance(javaTypeClass, typeHandlerClass));
292 }
293
294 // java type + jdbc type + handler type
295
296 public void register(Class> javaTypeClass, JdbcType jdbcType, Class> typeHandlerClass) {
297 register(javaTypeClass, jdbcType, getInstance(javaTypeClass, typeHandlerClass));
298 }
299
300 // Construct a handler (used also from Builders)
301
302 @SuppressWarnings("unchecked")
303 public TypeHandler getInstance(Class> javaTypeClass, Class> typeHandlerClass) {
304 if (javaTypeClass != null ) {
305 try {
306 Constructor> c = typeHandlerClass.getConstructor(Class.class );
307 return (TypeHandler) c.newInstance(javaTypeClass);
308 } catch (NoSuchMethodException ignored) {
309 // ignored
310 } catch (Exception e) {
311 throw new TypeException("Failed invoking constructor for handler " + typeHandlerClass, e);
312 }
313 }
314 try {
315 Constructor> c = typeHandlerClass.getConstructor();
316 return (TypeHandler) c.newInstance();
317 } catch (Exception e) {
318 throw new TypeException("Unable to find a usable constructor for " + typeHandlerClass, e);
319 }
320 }
321
322
323 /**
324 * 根据指定的pacakge去扫描自定义的typeHander,然后注册
325 */
326 public void register(String packageName) {
327 ResolverUtil> resolverUtil = new ResolverUtil>();
328 resolverUtil.find(new ResolverUtil.IsA(TypeHandler.class ), packageName);
329 Setextends Class>>> handlerSet = resolverUtil.getClasses();
330 for (Class> type : handlerSet) {
331 // Ignore inner classes and interfaces (including package-info.java) and abstract classes
332 if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
333 register(type);
334 }
335 }
336 }
337
338 // get information
339
340 /**
341 * 通过configuration对象可以获取已注册的所有typeHandler
342 */
343 public Collection> getTypeHandlers() {
344 return Collections.unmodifiableCollection(ALL_TYPE_HANDLERS_MAP.values());
345 }
346
347 }
由源码可以看到, mybatis为我们实现了那么多TypeHandler, 随便打开一个TypeHandler,看其源码,都可以看到,它继承自一个抽象类:BaseTypeHandler,
那么我们是不是也能通过继承BaseTypeHandler,从而实现自定义的TypeHandler ?
答案是肯定的
那么现在下面就为大家演示一下自定义TypeHandler:
=自定义TypeHandler分割线========
ExampleTypeHandler:
1 @MappedJdbcTypes(JdbcType.VARCHAR)
2 // 此处如果不用注解指定jdbcType, 那么,就可以在配置文件中通过"jdbcType"属性指定, 同理, javaType 也可通过 @MappedTypes指定
3 public class ExampleTypeHandler extends BaseTypeHandler {
4
5 @Override
6 public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
7 ps.setString(i, parameter);
8 }
9
10 @Override
11 public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
12 return rs.getString(columnName);
13 }
14
15 @Override
16 public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
17 return rs.getString(columnIndex);
18 }
19
20 @Override
21 public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
22 return cs.getString(columnIndex);
23 }
24 }
然后,就该配置我们的自定义TypeHandler了:
也就是说,我们在自定义TypeHandler的时候,可以在TypeHandler通过@MappedJdbcTypes指定jdbcType, 通过 @MappedTypes 指定javaType, 如果没有使用注解指定,那么我们就需要在配置文件中配置。
预知后事如何,请听下回分解。