1. 程式人生 > >自定義MyBatis返回Map物件

自定義MyBatis返回Map物件

最近一個專案要進行重構,需要把之前的ibatis轉為mybatis,其中有幾個方法是需要返回一個Map物件,我就到網上找方法,但找了半天,發現網上的好多都是同時指定Map的Key和Value,但現在專案需求是指指定Key值,而Value為實體類,於是我就把網上的方法進行了改善,程式碼如下:

1、MapParam.java

需要mybatis返回Map時需要指定引數型別為MapParam,可以通過建構函式單獨指定Key,也可以同時指定Key和Value屬性。

public class MapParam extends HashMap<String, Object> {
	private static final long serialVersionUID = 1L;

	private static final String KEY_FIELD = "_mapKeyField_";

	private static final String VALUE_FIELD = "_mapValueField_";

	public MapParam(String keyField) {
		this.put(KEY_FIELD, keyField);
	}

	public MapParam(String keyField, String valueField) {
		this.put(KEY_FIELD, keyField);
		this.put(VALUE_FIELD, valueField);
	}

	public String getKeyField() {
		return (String)this.get(KEY_FIELD);
	}

	public String getValueField() {
		return (String)this.get(VALUE_FIELD);
	}
}
2、MapInterceptor.java

攔截mybatis的結果集處理方法,進行自定義操作

@Intercepts(@Signature(method = "handleResultSets", type = ResultSetHandler.class, args = { Statement.class }))
public class MapInterceptor implements Interceptor {

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		Object target = invocation.getTarget();

		if (target instanceof FastResultSetHandler) {
			FastResultSetHandler handler = (FastResultSetHandler) target;

			ParameterHandler pHandler = Reflect.getFieldValue(handler,
					"parameterHandler");
			Object paramObj = pHandler.getParameterObject();

			if (paramObj instanceof MapParam) {
				MapParam param = (MapParam) paramObj;

				String keyField = param.getKeyField();
				String valueField = param.getValueField();
				if (valueField == null) {
					return handleKeyResult(invocation.proceed(), keyField);
				} else {
					Statement statement = (Statement) invocation.getArgs()[0];

					return handleResultSet(statement.getResultSet(), keyField,
							valueField);
				}
			}
		}

		return invocation.proceed();
	}

	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	@Override
	public void setProperties(Properties properties) {

	}

	private Object handleKeyResult(Object resultObj, String keyField) {
		List<?> list = (List<?>) resultObj;

		Map<Object, Object> map = new HashMap<Object, Object>();

		for (int i = 0; i < list.size(); i++) {
			Object obj = list.get(i);

			Object key = null;
			if (obj instanceof Map<?, ?>) {
				Map<?, ?> tmpMap = (Map<?, ?>) obj;
				key = (Object) tmpMap.get(keyField);
			} else {
				key = Reflect.getFieldValue(obj, keyField);
			}
			map.put(key, obj);
		}

		List<Object> resultList = new ArrayList<Object>();
		resultList.add(map);
		return resultList;
	}

	private Object handleResultSet(ResultSet resultSet, String keyField,
			String valueField) {
		if (resultSet != null) {
			// 定義用於存放Key-Value的Map
			Map<Object, Object> map = new HashMap<Object, Object>();
			// handleResultSets的結果一定是一個List,當我們的對應的Mapper介面定義的是返回一個單一的元素,並且handleResultSets返回的列表
			// 的size為1時,Mybatis會取返回的第一個元素作為對應Mapper介面方法的返回值。
			List<Object> resultList = new ArrayList<Object>();
			try {
				// 把每一行對應的Key和Value存放到Map中
				while (resultSet.next()) {
					Object key = resultSet.getObject(keyField);
					Object value = resultSet.getObject(valueField);
					map.put(key, value);
				}
			} catch (SQLException e) {
			} finally {
				closeResultSet(resultSet);
			}
			// 把封裝好的Map存放到List中並進行返回
			resultList.add(map);
			return resultList;
		}
		return null;
	}

	/**
	 * 關閉ResultSet
	 * 
	 * @param resultSet
	 *            需要關閉的ResultSet
	 */
	private void closeResultSet(ResultSet resultSet) {
		try {
			if (resultSet != null) {
				resultSet.close();
			}
		} catch (SQLException e) {

		}
	}
}
3、Reflect.java

通過反射方法,獲取攔截物件中的某些引數

public class Reflect {

	@SuppressWarnings("unchecked")
	public static <T> T getFieldValue(Object obj, String fieldName) {
		Object result = null;
		Field field = getField(obj, fieldName);
		if (field != null) {
			field.setAccessible(true);
			try {
				result = field.get(obj);
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
		return (T) result;
	}

	/**
	 * 利用反射獲取指定物件裡面的指定屬性
	 * 
	 * @param obj
	 *            目標物件
	 * @param fieldName
	 *            目標屬性
	 * @return 目標欄位
	 */
	private static Field getField(Object obj, String fieldName) {
		Field field = null;
		for (Class<?> clazz = obj.getClass(); clazz != Object.class; clazz = clazz
				.getSuperclass()) {
			try {
				field = clazz.getDeclaredField(fieldName);
				break;
			} catch (NoSuchFieldException e) {
				// 這裡不用做處理,子類沒有該欄位可能對應的父類有,都沒有就返回null。
			}
		}
		return field;
	}
}

4、註冊攔截器

在mybatis配置檔案中註冊自定義攔截器,由於mybatis配置檔案限制,各個標籤的位置必須按順序來,我之前不清楚,加進去之後配置檔案老是報錯

<plugins>
	<plugin interceptor="com.aspirecn.mcp.common.interceptor.MapInterceptor"></plugin>
</plugins>