Skip to content

Instantly share code, notes, and snippets.

@simonetripodi
Created December 16, 2011 09:41
Show Gist options
  • Save simonetripodi/1485359 to your computer and use it in GitHub Desktop.
Save simonetripodi/1485359 to your computer and use it in GitHub Desktop.
add advanced generic type handling in MyBatis
Index: src/test/java/org/apache/ibatis/type/TypeHandlerRegistryTest.java
===================================================================
--- src/test/java/org/apache/ibatis/type/TypeHandlerRegistryTest.java (revision 4287)
+++ src/test/java/org/apache/ibatis/type/TypeHandlerRegistryTest.java (working copy)
@@ -15,6 +15,13 @@
*/
package org.apache.ibatis.type;
+import java.net.URI;
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.List;
+
import domain.misc.RichType;
import static org.junit.Assert.*;
import org.junit.Test;
@@ -25,7 +32,7 @@
@Test
public void shouldRegisterAndRetrieveTypeHandler() {
- TypeHandler stringTypeHandler = typeHandlerRegistry.getTypeHandler(String.class);
+ TypeHandler<String> stringTypeHandler = typeHandlerRegistry.getTypeHandler(String.class);
typeHandlerRegistry.register(String.class, JdbcType.LONGVARCHAR, stringTypeHandler);
assertEquals(stringTypeHandler, typeHandlerRegistry.getTypeHandler(String.class, JdbcType.LONGVARCHAR));
@@ -36,4 +43,39 @@
assertTrue(typeHandlerRegistry.getUnknownTypeHandler() instanceof UnknownTypeHandler);
}
+ @Test
+ public void shouldRegisterAndRetrieveComplexTypeHandler() {
+ TypeHandler<List<URI>> fakeHandler = new TypeHandler<List<URI>>() {
+
+ public void setParameter( PreparedStatement ps, int i, List<URI> parameter, JdbcType jdbcType )
+ throws SQLException {
+ // do nothing, fake method
+ }
+
+ public List<URI> getResult( CallableStatement cs, int columnIndex )
+ throws SQLException {
+ // do nothing, fake method
+ return null;
+ }
+
+ public List<URI> getResult( ResultSet rs, int columnIndex )
+ throws SQLException {
+ // do nothing, fake method
+ return null;
+ }
+
+ public List<URI> getResult( ResultSet rs, String columnName )
+ throws SQLException {
+ // do nothing, fake method
+ return null;
+ }
+
+ };
+
+ TypeReference<List<URI>> type = new TypeReference<List<URI>>(){};
+
+ typeHandlerRegistry.register(type, fakeHandler);
+ assertSame(fakeHandler, typeHandlerRegistry.getTypeHandler(type));
+ }
+
}
Index: src/main/java/org/apache/ibatis/type/TypeReference.java
===================================================================
--- src/main/java/org/apache/ibatis/type/TypeReference.java (revision 0)
+++ src/main/java/org/apache/ibatis/type/TypeReference.java (revision 0)
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2009-2011 The MyBatis Team
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ibatis.type;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+
+/**
+ * References a generic type.
+ *
+ * @param <T> the referenced type
+ * @since 3.1.0
+ */
+public abstract class TypeReference<T> {
+
+ private final Type rawType;
+
+ protected TypeReference() {
+ Type superclass = getClass().getGenericSuperclass();
+ if ( superclass instanceof Class ) {
+ throw new RuntimeException( "Missing type parameter." );
+ }
+ rawType = ( (ParameterizedType) superclass ).getActualTypeArguments()[0];
+ }
+
+ public final Type getRawType() {
+ return rawType;
+ }
+
+}
Property changes on: src/main/java/org/apache/ibatis/type/TypeReference.java
___________________________________________________________________
Added: svn:mime-type
+ text/plain
Added: svn:keywords
+ Date Author Id Revision HeadURL
Added: svn:eol-style
+ native
Index: src/main/java/org/apache/ibatis/type/TypeHandlerRegistry.java
===================================================================
--- src/main/java/org/apache/ibatis/type/TypeHandlerRegistry.java (revision 4287)
+++ src/main/java/org/apache/ibatis/type/TypeHandlerRegistry.java (working copy)
@@ -18,6 +18,7 @@
import org.apache.ibatis.io.ResolverUtil;
import java.lang.reflect.Modifier;
+import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
@@ -41,7 +42,7 @@
};
private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<JdbcType, TypeHandler<?>>(JdbcType.class);
- private final Map<Class<?>, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new HashMap<Class<?>, Map<JdbcType, TypeHandler<?>>>();
+ private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new HashMap<Type, Map<JdbcType, TypeHandler<?>>>();
private final TypeHandler<Object> UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this);
public TypeHandlerRegistry() {
@@ -126,19 +127,39 @@
return hasTypeHandler(javaType, null);
}
+ public boolean hasTypeHandler(TypeReference<?> javaTypeReference) {
+ return hasTypeHandler(javaTypeReference, null);
+ }
+
public boolean hasTypeHandler(Class<?> javaType, JdbcType jdbcType) {
- return javaType != null && getTypeHandler(javaType, jdbcType) != null;
+ return javaType != null && getTypeHandler((Type) javaType, jdbcType) != null;
}
- public TypeHandler<?> getTypeHandler(Class<?> type) {
- return getTypeHandler(type, null);
+ public boolean hasTypeHandler(TypeReference<?> javaTypeReference, JdbcType jdbcType) {
+ return javaTypeReference != null && getTypeHandler(javaTypeReference, jdbcType) != null;
}
+ public <T> TypeHandler<T> getTypeHandler(Class<T> type) {
+ return getTypeHandler((Type) type, null);
+ }
+
+ public <T> TypeHandler<T> getTypeHandler(TypeReference<T> javaTypeReference) {
+ return getTypeHandler(javaTypeReference, null);
+ }
+
public TypeHandler<?> getTypeHandler(JdbcType jdbcType) {
return JDBC_TYPE_HANDLER_MAP.get(jdbcType);
}
- public TypeHandler<?> getTypeHandler(Class<?> type, JdbcType jdbcType) {
+ public <T> TypeHandler<T> getTypeHandler(Class<?> type, JdbcType jdbcType) {
+ return getTypeHandler( (Type) type, jdbcType );
+ }
+
+ public <T> TypeHandler<T> getTypeHandler(TypeReference<T> javaTypeReference, JdbcType jdbcType) {
+ return getTypeHandler( javaTypeReference.getRawType(), jdbcType );
+ }
+
+ private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(type);
TypeHandler<?> handler = null;
if (jdbcHandlerMap != null) {
@@ -147,10 +168,12 @@
handler = jdbcHandlerMap.get(null);
}
}
- if (handler == null && type != null && Enum.class.isAssignableFrom(type)) {
- handler = new EnumTypeHandler(type);
+ if (handler == null && type != null && type instanceof Class && Enum.class.isAssignableFrom((Class<?>) type)) {
+ handler = new EnumTypeHandler((Class<?>) type);
}
- return handler;
+ @SuppressWarnings( "unchecked" ) // type drives generics here
+ TypeHandler<T> returned = (TypeHandler<T>) handler;
+ return returned;
}
public TypeHandler<Object> getUnknownTypeHandler() {
@@ -162,6 +185,14 @@
}
public <T> void register(Class<T> type, TypeHandler<? extends T> handler) {
+ register((Type) type, handler);
+ }
+
+ public <T> void register(TypeReference<T> javaTypeReference, TypeHandler<? extends T> handler) {
+ register(javaTypeReference.getRawType(), handler);
+ }
+
+ public void register(Type type, TypeHandler<?> handler) {
MappedJdbcTypes mappedJdbcTypes = handler.getClass().getAnnotation(MappedJdbcTypes.class);
if (mappedJdbcTypes != null) {
for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
@@ -172,8 +203,9 @@
}
}
- public <T> void register(TypeHandler<? extends T> handler) {
+ public <T> void register(TypeHandler<T> handler) {
boolean mappedTypeFound = false;
+
MappedTypes mappedTypes = handler.getClass().getAnnotation(MappedTypes.class);
if (mappedTypes != null) {
for (Class<?> handledType : mappedTypes.value()) {
@@ -182,12 +214,37 @@
mappedTypeFound = true;
}
}
+
+ // @since 3.1.0 - try to auto-discover the mapped type
+ if (!mappedTypeFound && handler instanceof TypeReference)
+ {
+ try
+ {
+ @SuppressWarnings( "unchecked" )
+ TypeReference<T> typeReference = (TypeReference<T>) handler;
+ register( typeReference, handler );
+ mappedTypeFound = true;
+ } catch (Throwable t) {
+ // maybe users define the TypeReference with a different type and are not assignable, so just ignore it
+ }
+ }
+
if (!mappedTypeFound) {
- throw new RuntimeException("Unable to get mapped types, check @MappedTypes annotation for type handler " + handler);
+ throw new RuntimeException("Unable to get mapped types, check @MappedTypes annotation for type handler "
+ + handler
+ + " or TypeReference<T> raw type");
}
}
- public void register(Class<?> type, JdbcType jdbcType, TypeHandler<?> handler) {
+ public <T> void register(Class<T> type, JdbcType jdbcType, TypeHandler<? extends T> handler) {
+ register((Type) type, jdbcType, handler);
+ }
+
+ public <T> void register(TypeReference<T> javaTypeReference, JdbcType jdbcType, TypeHandler<? extends T> handler) {
+ register(javaTypeReference.getRawType(), jdbcType, handler);
+ }
+
+ public void register(Type type, JdbcType jdbcType, TypeHandler<?> handler) {
Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(type);
if (map == null) {
map = new HashMap<JdbcType, TypeHandler<?>>();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment