关于mybatis拦截器,对结果集进行拦截
因业务需要,需将结果集序列化为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,调用下一个拦截器拦截目标方法。