Mybatis的TypeHandler的一个坑

有一个需求是自动扫描项目中的枚举类,然后注册枚举处理器,遇见了这个问题:枚举也都扫描到了,也注册到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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值