一、問題說明
Oracle中,查詢結果字段默認大寫。
高斯中,查詢結果字段默認小寫。
在Mybatis的xml中,如果查詢語句使用Map接收查詢結果,使用resultType="java.util.HashMap"
或resultType="Map"
等寫法,返回的Map對象中,Key就是字段名,從Oracle遷移到高斯,大寫變成小寫,存在兼容性問題。
二、解決辦法
方案1、使用字段別名
當代碼中使用Map接收查詢結果使用比較少時,可以直接修改sql,通過為字段指定別名的方式,實現最終字段名為大寫,如上面演示的user_id AS "USER_ID"
。
方案2、自定義Mybatis攔截器
通過mybatis攔截器實現查詢結果返回后,如果通過Map類型接收查詢結果,將Key轉為大寫。
1. 創建自定義mybatis攔截器
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;import java.lang.reflect.Method;
import java.util.*;/*** @desc Map接收查詢結果,將Map的Key轉為大寫,解決從Oracle切換高斯后,高斯默認小寫,JSP字段綁定異常,生成推送文件字段變化等問題* {@link MapKeyUpperCase}*/
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class,ResultHandler.class})})
public class MapKeyUpperCaseInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 執行原方法,獲取返回結果Object result = invocation.proceed();// 獲取當前執行的Mapper方法,Executor.query的第一個參數為MappedStatementMappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];// 方法全路徑:com.example.mapper.UserMapper.selectUserMapString methodName = mappedStatement.getId();// 通過反射獲取Mapper接口的方法,檢查是否有自定義注解String className = methodName.substring(0, methodName.lastIndexOf("."));String simpleMethodName = methodName.substring(methodName.lastIndexOf(".") + 1);Class<?> mapperInterface = Class.forName(className);Method method = Arrays.stream(mapperInterface.getMethods()).filter(m -> m.getName().equals(simpleMethodName)).findFirst().orElse(null);// 只對配置了自定義注解的方法生效if (method != null && method.isAnnotationPresent(MapKeyUpperCase.class)) {// 返回結果是List,處理每一項if (result instanceof List) {List<Map<?, ?>> resultList = (List<Map<?, ?>>) result;for (Map<?, ?> map : resultList) {// 原記錄索引替換為新記錄resultList.set(resultList.indexOf(map), upCaseResultMapKey(map));}return resultList;}// 返回單條記錄else if (result instanceof Map) {return upCaseResultMapKey((Map<?, ?>) result);}}return result;}/*** @param resultMap 初始查詢結果* @return java.util.HashMap<java.lang.Object, java.lang.Object>* @desc 將Map接收的查詢結果的Key轉為大寫*/private HashMap<Object, Object> upCaseResultMapKey(Map<?, ?> resultMap) {HashMap<Object, Object> newMap = new HashMap<>();for (Map.Entry<?, ?> entry : resultMap.entrySet()) {Object key = entry.getKey();Object value = entry.getValue();if (key instanceof String) {newMap.put(((String) key).toUpperCase(), value);} else {newMap.put(key, value);}}return newMap;}@Overridepublic Object plugin(Object target) {if (target instanceof Executor) {return Plugin.wrap(target, this);}return target;}@Overridepublic void setProperties(Properties properties) {}}
2. 創建自定義注解
import java.lang.annotation.*;
/*** @desc 聲明在Mapper中以Map接收查詢結果的查詢方法上,標明該方法返回字段名稱大寫* {@link MapKeyUpperCaseInterceptor}*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MapKeyUpperCase {
}
3. 注冊Mybatis攔截器
傳統方式,在mybatis-config.xml中注冊攔截器
<configuration><plugins><plugin interceptor="com.example.MapKeyUpperCaseInterceptor"></plugin></plugins>
</configuration>
在Springboot中注冊Mybatis攔截器,參考下面代碼
@Configuration
public class MyBatisConfig {@Beanpublic MapKeyUpperCaseInterceptor mapKeyUpperCaseInterceptor() {return new MapKeyUpperCaseInterceptor();}@Beanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource, MapKeyUpperCaseInterceptor mapKeyUpperCaseInterceptor) throws Exception {SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();factoryBean.setDataSource(dataSource);factoryBean.setPlugins(mapKeyUpperCaseInterceptor); // 注冊攔截器return factoryBean.getObject();}
}
4. 在需要將Map中的Key轉為大寫的方法上,聲明自定義注解
在需要大寫的mapper方法上聲明該注解,實現返回Map對象Key是否大寫的配置,如
@MapKeyUpperCase
Map<String, Object> getMap();@MapKeyUpperCase
List<Map<String, Object>> getMapList();@MapKeyUpperCase
Page<HashMap<String, String>> queryMapByPage(RowBounds rb);