1.需求背景
設(shè)定訂單表order暖呕,要根據(jù)訂單類(lèi)型統(tǒng)計(jì)訂單數(shù)據(jù)勾栗,大致sql如下:
select order_type , count(1) as order_num from order group by order_type;
Mybatis無(wú)法將以上sql以指定key:order_type;value:order_num
存入至map中。
而Mybatis默認(rèn)返回的List<Map<String, Object>>
遏插,是以每個(gè)字段name作為key调煎,字段的值作為value蔫磨,放入至Map<String,Object>
的List數(shù)組纳寂。
因此自定義一種可以指定key主穗、value字段的Mybatis插件將非常有用。
2.自定義Mybatis攔截器實(shí)現(xiàn)Mybaits Map返回類(lèi)型
有關(guān)于Mybatis攔截器的介紹請(qǐng)參閱Mybatis攔截器毙芜;
由Mybatis源碼可知忽媒,返回結(jié)果集是ResultSetHandler接口的handleResultSets方法實(shí)現(xiàn)的,源碼如下:
public interface ResultSetHandler {
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
當(dāng)我們需要返回Map時(shí)腋粥,只需要對(duì)此方法進(jìn)行攔截晦雨,重新組裝返回結(jié)果數(shù)據(jù);當(dāng)需要攔截時(shí)隘冲,執(zhí)行invocation.proceed()
闹瞧。首先我們定義MapParam.java類(lèi),用去標(biāo)識(shí)結(jié)果集需要攔截展辞,此外在該類(lèi)中指定返回結(jié)果集Map的key和value名稱(chēng)奥邮,以及value返回類(lèi)型。MapParam代碼如下:
public class MapParam extends HashMap {
// key名稱(chēng)
public static final String KEY_FIELD = "keyField";
// value名稱(chēng)
public static final String VALUE_FIELD = "valueField";
// value值類(lèi)型
public static final String VALUE_CLASS = "valueClass";
public MapParam(){ }
public MapParam(String keyField, String valueField, String valueClass){
this.put(KEY_FIELD, keyField);
this.put(VALUE_FIELD, valueField);
this.put(VALUE_CLASS, valueClass); }
// value值類(lèi)型枚舉類(lèi)
public enum ValueClass {
INTEGER("integer"),
BIG_DECIMAL("bigDecimal");
private String code;
public String getCode() {
return code;
}
ValueClass(String code){
this.code = code;
}
}
}
通過(guò)類(lèi)MapParam罗珍,我們可以定義key和value的字段值洽腺,還可以定義value的值類(lèi)型;
接下來(lái)覆旱,我們定義MapInterceptor.java蘸朋,通過(guò)對(duì)返回結(jié)果集方法handleResultSets攔截,返回需要指定的Map數(shù)據(jù)扣唱。少說(shuō)廢話(huà)藕坯,直接上代碼:
@Intercepts(@Signature(method="handleResultSets", type=ResultSetHandler.class, args={Statement.class}))
public class MapInterceptor implements Interceptor {
//日志 private static final Logger logger = LoggerFactory.getLogger(MapInterceptor.class);
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 獲取代理目標(biāo)對(duì)象
Object target = invocation.getTarget();
if (target instanceof DefaultResultSetHandler) {
DefaultResultSetHandler resultSetHandler = (DefaultResultSetHandler) target;
// 利用反射獲取參數(shù)對(duì)象
ParameterHandler parameterHandler = reflect(resultSetHandler);
Object parameterObj = parameterHandler.getParameterObject();
// 參數(shù)對(duì)象為MapParam進(jìn)入處理邏輯
if (parameterObj instanceof MapParam) {
MapParam mapParam = (MapParam) parameterObj;
// 獲取當(dāng)前statement
Statement stmt = (Statement) invocation.getArgs()[0];
// 根據(jù)maoParam返回處理結(jié)果
return handleResultSet(stmt.getResultSet(), mapParam);
}
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
private Object handleResultSet(ResultSet resultSet, MapParam mapParam){
if (null != resultSet){
// 獲取key field name
String keyFieldName = (String)mapParam.get(MapParam.KEY_FIELD);
// 獲取value field name
String valueFieldName = (String)mapParam.get(MapParam.VALUE_FIELD);
// 值類(lèi)型
String valueClass = (String) mapParam.get(MapParam.VALUE_CLASS);
List<Object> resultList = new ArrayList<Object>();
Map<Object, Object> map = new HashMap<Object, Object>();
try {
while (resultSet.next()) {
Object key = resultSet.getObject(keyFieldName);
Object value ;
// 根據(jù)值類(lèi)型轉(zhuǎn)換值
if (StringUtils.equals(valueClass, MapParam.ValueClass.INTEGER.getCode())) {
value = resultSet.getInt(valueFieldName);
} else if(StringUtils.equals(valueClass, MapParam.ValueClass.BIG_DECIMAL.getCode())) {
value = resultSet.getBigDecimal(valueFieldName);
} else {
value = resultSet.getObject(valueFieldName);
}
map.put(key, value);
}
} catch (SQLException e) {
logger.error("map interceptor轉(zhuǎn)換異常,{}", e.getMessage());
} finally {
// 關(guān)閉result set
closeResultSet(resultSet);
}
resultList.add(map);
return resultList;
}
return null;
}
private void closeResultSet(ResultSet resultSet) {
try {
if (resultSet != null) {
resultSet.close();
}
} catch (SQLException e) {
logger.error("關(guān)閉 result set異常,{}", e.getMessage());
}
}
private ParameterHandler reflect(DefaultResultSetHandler resultSetHandler){
Field field = ReflectionUtils.findField(DefaultResultSetHandler.class, "parameterHandler");
field.setAccessible(true);
Object value = null;
try {
value = field.get(resultSetHandler);
} catch (Exception e) {
logger.error("默認(rèn)返回結(jié)果集反射參數(shù)對(duì)象異常噪沙,{}", e.getMessage());
}
return (ParameterHandler)value;
}
@Intercepts(@Signature(method="handleResultSets", type=ResultSetHandler.class, args={Statement.class}))
注解代碼含義炼彪,@Intercepts用于表示該類(lèi)為攔截器,@Signature用于標(biāo)識(shí)需要攔截的方法名曲聂、返回類(lèi)型及方法參數(shù)值霹购。
進(jìn)入ResultSetHandler的handleResultSets方法,都會(huì)進(jìn)入此攔截器朋腋,當(dāng)需要的請(qǐng)求參數(shù)為類(lèi)MapParam時(shí)齐疙,執(zhí)行處理,否則執(zhí)行invocation.proceed()
旭咽。
代碼寫(xiě)的很詳細(xì)贞奋,再次無(wú)需再重復(fù)講述。
3.具體實(shí)現(xiàn)
注冊(cè)攔截器:
<bean id="sqlSessionFactory"
<property name="plugins">
<array>
<bean class="com.test.common.interceptor.MapInterceptor"/>
</array>
</property>
</bean>
dao層代碼:
MapParam params = new MapParam("orderType","orderNum",MapParam.ValueClass.INTEGER.getCode());
Map<String,Integer> find(MapParam params);
xml代碼:
<select id="find" resultType="map" parameterType="MapParam">
select order_type as orderType , count(1) as orderNum from order group by order_type;
</select>
打印結(jié)果格式:
“mobileOrder”:“100”
“partsOrder”:“200”
“normalOrder”:“500”