关于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,调用下一个拦截器拦截目标方法。

             

原文地址:https://www.cnblogs.com/SmallHan/p/8127327.html