有一个需求是自动扫描项目中的枚举类,然后注册枚举处理器,遇见了这个问题:枚举也都扫描到了,也注册到configuration当中去了,但是,查询的时候还是报错了!
mapper.xml:
<resultMap id="PersonMap" type="Person">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="gender" property="gender" typeHandler="EnumHandler"/><!-- typeHandler="EnumHandler" -->
<result column="addr_id" property="addrId"/>
<association property="addr" resultMap="AddressMap" />
</resultMap>
<resultMap id="AddressMap" type="Address">
<id column="a_id" property="id"/>
<result column="addr" property="addr"/>
<result column="status" property="status" typeHandler="EnumHandler"/><!-- typeHandler="EnumHandler" -->
</resultMap>
跟踪mapper的解析过程,首先会进行如下的操作:
如果没有在配置文件中显示的配置typeHandler就直接返回,如果显示的配置了,则首先从typeHandlerRegistry中根据typeHandler的类型来取。
这里就有问题了:假如我们的多个bean用的是同一个typeHandler,显然这里就会出问题啊!因为无论typeHandler注册了多少个bean,ALL_TYPE_HANDLERS_MAP中始终只有一个TypeHandler实例!
因此,如果存在多个bean共用用一个typeHandler的情况,则一定不要在配置文件中显式的手动指定。
那么,问题又来了,如果不手动指定又该怎么搞呢?从上面的代码其实也能看出来,如果根据bean的类型从TYPE_HANDLER_MAP这里面获取则是正确的。
接着往下看:
这里是根据列实际的class来查找typeHandler。这才是正确的逻辑。因此,只要提前把类和类对应的typeHandler注册进来就ok了。
假如多个类用了同一个typeHandler,但是又想在mapper中显式设置typeHandler又咋办呢?那就只能反射了:
try{
Field field = TypeHandlerRegistry.class.getDeclaredField("ALL_TYPE_HANDLERS_MAP");
field.setAccessible(true);
Map<Class<?>, TypeHandler<?>> map = (Map<Class<?>, TypeHandler<?>>)field.get(registry);
map.remove(EnumHandler.class);
}catch(Exception e){
e.printStackTrace();
}
附一个枚举自动扫描的代码:
protected static SqlSessionFactory buildSqlSessionFactory() throws Exception {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, null, null);
Configuration config = parser.getConfiguration();
scanEnums(config, basePackage);
parser.parse();
return new DefaultSqlSessionFactory(config);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
}
}
}
private static void scanEnums(Configuration configuration, String basePackage) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
resolverUtil.find(new ResolverUtil.IsA(Identifiable.class), basePackage);
Set<Class<? extends Class<?>>> mTypes = resolverUtil.getClasses();
for (Class<?> javaTypeClass : mTypes) {
System.out.println("scan enum:" + javaTypeClass);
registerEnumHandler(configuration, javaTypeClass);
}
}
private static void registerEnumHandler(org.apache.ibatis.session.Configuration configuration,
Class<?> javaTypeClass) {
if (javaTypeClass == Identifiable.class) {
return;
}
TypeHandlerRegistry registry = configuration.getTypeHandlerRegistry();
registry.register(javaTypeClass, EnumHandler.class);
// try{
// Field field = TypeHandlerRegistry.class.getDeclaredField("ALL_TYPE_HANDLERS_MAP");
// field.setAccessible(true);
// Map<Class<?>, TypeHandler<?>> map = (Map<Class<?>, TypeHandler<?>>)field.get(registry);
// map.remove(EnumHandler.class);
// }catch(Exception e){
// e.printStackTrace();
// }
}
源码在这里: https://github.com/xjs1919/enumhandler