Mybatis的TypeHandler的一个坑

有一个需求是自动扫描项目中的枚举类,然后注册枚举处理器,遇见了这个问题:枚举也都扫描到了,也注册到configuration当中去了,但是,查询的时候还是报错了!

mapper.xml:


		
		
		
		
		
	
	
	
		
		
		
	

跟踪mapper的解析过程,首先会进行如下的操作:

Mybatis的TypeHandler的一个坑_第1张图片

Mybatis的TypeHandler的一个坑_第2张图片
Mybatis的TypeHandler的一个坑_第3张图片

Mybatis的TypeHandler的一个坑_第4张图片

Mybatis的TypeHandler的一个坑_第5张图片

Mybatis的TypeHandler的一个坑_第6张图片

Mybatis的TypeHandler的一个坑_第7张图片

Mybatis的TypeHandler的一个坑_第8张图片

Mybatis的TypeHandler的一个坑_第9张图片

如果没有在配置文件中显示的配置typeHandler就直接返回,如果显示的配置了,则首先从typeHandlerRegistry中根据typeHandler的类型来取。

这里就有问题了:假如我们的多个bean用的是同一个typeHandler,显然这里就会出问题啊!因为无论typeHandler注册了多少个bean,ALL_TYPE_HANDLERS_MAP中始终只有一个TypeHandler实例!

因此,如果存在多个bean共用用一个typeHandler的情况,则一定不要在配置文件中显式的手动指定。

那么,问题又来了,如果不手动指定又该怎么搞呢?从上面的代码其实也能看出来,如果根据bean的类型从TYPE_HANDLER_MAP这里面获取则是正确的。

接着往下看:

Mybatis的TypeHandler的一个坑_第10张图片

Mybatis的TypeHandler的一个坑_第11张图片

Mybatis的TypeHandler的一个坑_第12张图片

这里是根据列实际的class来查找typeHandler。这才是正确的逻辑。因此,只要提前把类和类对应的typeHandler注册进来就ok了。


假如多个类用了同一个typeHandler,但是又想在mapper中显式设置typeHandler又咋办呢?那就只能反射了:

try{
            Field field = TypeHandlerRegistry.class.getDeclaredField("ALL_TYPE_HANDLERS_MAP");
            field.setAccessible(true);
            Map, TypeHandler> map = (Map, 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> resolverUtil = new ResolverUtil>();
		resolverUtil.find(new ResolverUtil.IsA(Identifiable.class), basePackage);
		Set>> 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, TypeHandler> map = (Map, TypeHandler>)field.get(registry);
//            map.remove(EnumHandler.class);
//        }catch(Exception e){
//            e.printStackTrace();
//        }
	}

源码在这里: https://github.com/xjs1919/enumhandler



你可能感兴趣的:(java)