因業務需要,需將結果集序列化為json返回,於是,網上找了好久資料,都是關於攔截參數的處理,攔截Sql語法構建的處理,就是很少關於對攔截結果集的處理,於是自己簡單的寫了一個對結果集的處理,
記錄下。
一、MyBatis的框架設計圖

參考:http://blog.csdn.net/luanlouis/article/details/40422941
1.如何將結果集改成我們想要的格式呢?
1.1 由原理圖我們可知,ResultSetHandler負責將resultSet轉換為list,那么我們能不能在轉換的時候加上自己的邏輯,我想應該是可以的,但是因為源碼看不太懂,想想還是算了。
1 public List<Object> handleResultSets(Statement stmt) throws SQLException { 2 final List<Object> multipleResults = new ArrayList<Object>(); 3 4 int resultSetCount = 0; 5 ResultSetWrapper rsw = getFirstResultSet(stmt); 6 7 List<ResultMap> resultMaps = mappedStatement.getResultMaps(); 8 int resultMapCount = resultMaps.size(); 9 validateResultMapsCount(rsw, resultMapCount); 10 11 while (rsw != null && resultMapCount > resultSetCount) { 12 ResultMap resultMap = resultMaps.get(resultSetCount); 13 14 //將resultSet 15 handleResultSet(rsw, resultMap, multipleResults, null); 16 rsw = getNextResultSet(stmt); 17 cleanUpAfterHandlingResultSet(); 18 resultSetCount++; 19 } 20 21 String[] resultSets = mappedStatement.getResulSets(); 22 if (resultSets != null) { 23 while (rsw != null && resultSetCount < resultSets.length) { 24 ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]); 25 if (parentMapping != null) { 26 String nestedResultMapId = parentMapping.getNestedResultMapId(); 27 ResultMap resultMap = configuration.getResultMap(nestedResultMapId); 28 handleResultSet(rsw, resultMap, null, parentMapping); 29 } 30 rsw = getNextResultSet(stmt); 31 cleanUpAfterHandlingResultSet(); 32 resultSetCount++; 33 } 34 } 35 36 return collapseSingleResultList(multipleResults); 37 }
1.2 排除第一種方案,那么能不能跳過這個方法執行我們的自己的邏輯,答案是可以的,接下來我們會用到mybatis的攔截器。
2.定義mybatis攔截器,用來攔截handleResultSets
2.1 我們需要拿到MappedStatement(維護了一條<select|update|delete|insert>節點的封裝)這個對象,才能獲得resultType是什么類型,用於判斷,那我們該怎么獲取這個對象呢? 首先我們到方法handleResultSets所屬類中的源碼里面看看 。
(1) MappedStatement 是怎么生成的呢?由源碼我們可知,是用過構造函數賦值的
1 public DefaultResultSetHandler(Executor executor, MappedStatement mappedStatement, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql, 2 RowBounds rowBounds) { 3 this.executor = executor; 4 this.configuration = mappedStatement.getConfiguration(); 5 this.mappedStatement = mappedStatement; 6 this.rowBounds = rowBounds; 7 this.parameterHandler = parameterHandler; 8 this.boundSql = boundSql; 9 this.typeHandlerRegistry = configuration.getTypeHandlerRegistry(); 10 this.objectFactory = configuration.getObjectFactory(); 11 this.resultHandler = resultHandler; 12 }
(2) 那么既然存在這個屬性,就能獲取到這個對象么,可是,當我嘗試去找獲得 MappedStatement這個對象的方法時,並未找到,所以我只能自己加上去了!
1 public MappedStatement getMappedStatement() { 2 return mappedStatement; 3 }
2.1 我們在攔截器中就能使用MappedStatement 對象,從而獲得resultType的類型,為什么要這樣做呢?因為我只想實現當resultType為String的時候,才執行我自己的邏輯。
1 @Intercepts({ @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = { Statement.class }) }) 2 public class JsonPlugin implements Interceptor { 3 4 public Object intercept(Invocation invocation) throws Throwable { 5 List<String> resList = new ArrayList<String>(); 6 7 DefaultResultSetHandler defaultResultSetHandler = (DefaultResultSetHandler) invocation.getTarget(); 8 //MappedStatement維護了一條<select|update|delete|insert>節點的封裝 9 MappedStatement mappedStatement = defaultResultSetHandler.getMappedStatement(); 10 //獲取節點屬性的集合 11 List<ResultMap> resultMaps = mappedStatement.getResultMaps(); 12 int resultMapCount = resultMaps.size(); 13 //獲取當前resutType的類型 14 Class<?> resultType = resultMaps.get(0).getType(); 15 if (resultMapCount > 0 && resultType.getName().equals("java.lang.String")) { 16 Object[] obj = invocation.getArgs(); 17 Statement statement = (Statement) invocation.getArgs()[0]; 18 //獲得結果集 19 ResultSet resultSet = statement.getResultSet(); 20 21 if (resultSet != null) { 22 //獲得對應列名 23 ResultSetMetaData rsmd = resultSet.getMetaData(); 24 List<String> columnList = new ArrayList<String>(); 25 26 for (int i = 1; i <= rsmd.getColumnCount(); i++) { 27 columnList.add(rsmd.getColumnName(i)); 28 } 29 while (resultSet.next()) { 30 LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>(); 31 for (String colName : columnList) { 32 map.put(colName, resultSet.getObject(colName)); 33 } 34 JSONObject.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd hh:mm:ss";// 設置日期格式 35 resList.add(JSON.toJSONString(map, SerializerFeature.WriteMapNullValue, 36 SerializerFeature.DisableCircularReferenceDetect, 37 SerializerFeature.WriteDateUseDateFormat)); 38 } 39 return resList; 40 } 41 } 42 return invocation.proceed(); 43 } 44 45 public Object plugin(Object target) { 46 // 讀取@Signature中的配置,判斷是否需要生成代理類 47 if (target instanceof ResultSetHandler) { 48 return Plugin.wrap(target, this); 49 } else { 50 return target; 51 } 52 } 53 54 public void setProperties(Properties properties) { 55 56 }
2.2 加入攔截器配置
<!-- 攔截器配置開始 -->
<plugins>
<plugin interceptor="com.smallhan.base.interceptor.JsonPlugin" />
</plugins>
3.測試結果
3.1 ResultType為String的時候,如下圖所示,並且繞過handleResultSets

3.2 ResultType為其他類型的時候,跳過我們自己寫的邏輯,執行invocation.proceed,調用下一個攔截器攔截目標方法。

