关于mybatis拦截器,对结果集进行拦截
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
关于mybatis拦截器,对结果集进⾏拦截
因业务需要,需将结果集序列化为json返回,于是,⽹上找了好久资料,都是关于拦截参数的处理,拦截Sql语法构建的处理,就是很少关于对拦截结果集的处理,于是⾃⼰简单的写了⼀个对结果集的处理,
记录下。
⼀、MyBatis的框架设计图
参考:/luanlouis/article/details/40422941
1.如何将结果集改成我们想要的格式呢?
1.1 由原理图我们可知,ResultSetHandler负责将resultSet转换为list,那么我们能不能在转换的时候加上⾃⼰的逻辑,我想应该是可以的,但是因为源码看不太懂,想想还是算了。
1public List<Object> handleResultSets(Statement stmt) throws SQLException {
2final List<Object> multipleResults = new ArrayList<Object>();
3
4int resultSetCount = 0;
5 ResultSetWrapper rsw = getFirstResultSet(stmt);
6
7 List<ResultMap> resultMaps = mappedStatement.getResultMaps();
8int resultMapCount = resultMaps.size();
9 validateResultMapsCount(rsw, resultMapCount);
10
11while (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();
22if (resultSets != null) {
23while (rsw != null && resultSetCount < resultSets.length) {
24 ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
25if (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
36return collapseSingleResultList(multipleResults);
37 }
1.2 排除第⼀种⽅案,那么能不能跳过这个⽅法执⾏我们的⾃⼰的逻辑,答案是可以的,接下来我们会⽤到mybatis的拦截器。
2.定义mybatis拦截器,⽤来拦截handleResultSets
2.1 我们需要拿到MappedStatement(维护了⼀条<select|update|delete|insert>节点的封装)这个对象,才能获得resultType是什么类型,⽤于判断,那我们该怎么获取这个对象呢?⾸先我们到⽅法handleResultSets所属类中的源码⾥⾯看看。
(1) MappedStatement 是怎么⽣成的呢?由源码我们可知,是⽤过构造函数赋值的
1public DefaultResultSetHandler(Executor executor, MappedStatement mappedStatement, ParameterHandler parameterHandler, ResultHandler resultHandler, BoundSql boundSql, 2 RowBounds rowBounds) {
3this.executor = executor;
4this.configuration = mappedStatement.getConfiguration();
5this.mappedStatement = mappedStatement;
6this.rowBounds = rowBounds;
7this.parameterHandler = parameterHandler;
8this.boundSql = boundSql;
9this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
10this.objectFactory = configuration.getObjectFactory();
11this.resultHandler = resultHandler;
12 }
(2) 那么既然存在这个属性,就能获取到这个对象么,可是,当我尝试去找获得 MappedStatement这个对象的⽅法时,并未找到,所以我只能⾃⼰加上去了!
1public MappedStatement getMappedStatement() {
2return mappedStatement;
3 }
2.1 我们在拦截器中就能使⽤MappedStatement 对象,从⽽获得resultType的类型,为什么要这样做呢?因为我只想实现当resultType为String的时候,才执⾏我⾃⼰的逻辑。
1 @Intercepts({ @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = { Statement.class }) })
2public class JsonPlugin implements Interceptor {
3
4public 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();
12int resultMapCount = resultMaps.size();
13//获取当前resutType的类型
14 Class<?> resultType = resultMaps.get(0).getType();
15if (resultMapCount > 0 && resultType.getName().equals("ng.String")) {
16 Object[] obj = invocation.getArgs();
17 Statement statement = (Statement) invocation.getArgs()[0];
18//获得结果集
19 ResultSet resultSet = statement.getResultSet();
20
21if (resultSet != null) {
22//获得对应列名
23 ResultSetMetaData rsmd = resultSet.getMetaData();
24 List<String> columnList = new ArrayList<String>();
25
26for (int i = 1; i <= rsmd.getColumnCount(); i++) {
27 columnList.add(rsmd.getColumnName(i));
28 }
29while (resultSet.next()) {
30 LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();
31for (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 }
39return resList;
40 }
41 }
42return invocation.proceed();
43 }
44
45public Object plugin(Object target) {
46// 读取@Signature中的配置,判断是否需要⽣成代理类
47if (target instanceof ResultSetHandler) {
48return Plugin.wrap(target, this);
49 } else {
50return target;
51 }
52 }
53
54public 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,调⽤下⼀个拦截器拦截⽬标⽅法。