Mybatis拦截器,解决达梦数据库sql返回类型Map key大写问题

Mybatis拦截器,解决达梦数据库sql返回类型Map key大写问题

拦截器功能点代码介绍

在代码中有一句invocation.proceed();方法,如果执行了这个方法,就是mybatis自己处理查询结果返给我们用。如果不执行该方法,则我们自己可以拿到结果自己映射到返回类型中去

拦截器入口


import org.apache.ibatis.executor.resultset.DefaultResultSetHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.stereotype.Component;

import java.sql.Statement;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * 拦截特定接口的返回值,转成Map
 */
@Component
//拦截Executor类的query方法
@Intercepts({@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})})
public class MybatisResultIntercept implements Interceptor {


    public Object intercept(Invocation invocation) throws Throwable {
        DefaultResultSetHandler defaultResultSetHandler = (DefaultResultSetHandler) invocation.getTarget();
        MetaObject metaStatementHandler = SystemMetaObject.forObject(defaultResultSetHandler);
        MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("mappedStatement");
        //获取节点属性的集合
        List<ResultMap> resultMaps = mappedStatement.getResultMaps();
        //上面一堆代码主要是为了获取sql的映射类型
        Class<?> resultType = resultMaps.get(0).getType();
        //InterceptorFlagInterface这个是我自己定义的一个标记接口,这句代码意思是,如果sql的结果映射类型为map或者实体实现了InterceptorFlagInterface接口的都会走这个if语句
        if (Map.class.isAssignableFrom(resultType) || InterceptorFlagInterface.class.isAssignableFrom(resultType)) {
            //处理sql返回值是Map类型的
            return MapIntercept.interceptMap(invocation);
        } else {
            return EntityIntercept.intercept(invocation);
        }
    }

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

    public void setProperties(Properties arg0) {
    }
}

接收Mybatis处理的结果

这种是接收框架拼接的结果,处理达梦数据库Map key大写问题
这个类是拦截器类,里面有2中业务处理方法,是我自己的业务


import com.alibaba.druid.pool.DruidPooledResultSet;
import com.alibaba.druid.pool.DruidPooledStatement;
import com.alibaba.druid.proxy.jdbc.NClobProxyImpl;
import com.alibaba.druid.proxy.jdbc.PreparedStatementProxyImpl;
import com.alibaba.fastjson.JSONObject;
import dm.jdbc.desc.Column;
import dm.jdbc.driver.DmdbNClob;
import dm.jdbc.driver.DmdbPreparedStatement;
import org.apache.ibatis.plugin.Invocation;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 拦截特定接口的返回值,转成Map
 */

public class MapIntercept {
    /**
     * 处理拦截map
     *
     * @param invocation
     * @return
     * @throws Throwable
     */
    public static Object interceptMap(Invocation invocation) throws Throwable {
        //执行请求方法,框架所得结果保存到result中
        Object result = invocation.proceed();
        if (result != null && result instanceof ArrayList) {
            ArrayList resultList = (ArrayList) result;
            //当前返回值的实体类是实现了InterceptorFlagInterface的才会做转换
            if (resultList.size() != 0 && resultList.get(0) instanceof InterceptorFlagInterface) {
                //这个方法不用看,是处理Map key大写的备用方案
                List<Map> res = new ArrayList<>();
                for (int i = 0; i < resultList.size(); i++) {
                    res.add(JSONObject.parseObject(JSONObject.toJSONString(resultList.get(i)), Map.class));
                }
                return res;
            } else if (resultList.size() != 0 && resultList.get(0) instanceof Map) {
                //将key大写的map转为小写
                return handelResultMap(resultList, getColumns(invocation));
            }

        }
        return result;
    }

    private static Object handelResultMap(ArrayList resultList, Column[] columns) {
        Map<String, String> columnsToMap = columnsToMap(columns);
        List<Map> res = new ArrayList<>();
        for (int i = 0; i < resultList.size(); i++) {
            Map<String, Object> oldMap = (Map<String, Object>) resultList.get(i);
            Map<String, Object> newMap = new HashMap<>(oldMap.size());
            for (Map.Entry<String, Object> entry : oldMap.entrySet()) {
                Object value = entry.getValue();
                //如果数据库类型为clob或blob,sql映射类型为map,需要单独处理
                if (value instanceof NClobProxyImpl) {
                    NClobProxyImpl nClobProxy = (NClobProxyImpl) value;
                    DmdbNClob rawClob = (DmdbNClob) nClobProxy.getRawClob();
                    value = rawClob.data;
                }/*else if (value instanceof DmdbBlob) {
                    //处理blob,当前代码实验未成功
                    DmdbBlob dmdbBlob = (DmdbBlob)value;
                    try {
                        byte[] data = dmdbBlob.data;
                        byte[] bytes = "blob".getBytes();
                        value = new String(dmdbBlob.data, "UTF-8");
                        value = bytes2HexString(data);
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                }*/

                if (columnsToMap.get(entry.getKey()) == null) {
                    newMap.put(entry.getKey(), value);
                } else {
                //将大写key变为我们正确的列名
                    newMap.put(columnsToMap.get(entry.getKey()), value);
                }
            }
            res.add(newMap);

        }

        return res;
    }

    /**
     * 当sql的返回值是Map类型时,需要将大写的key转成小写的key
     *
     * @param columns
     * @return
     */
    public static Map<String, String> columnsToMap(Column[] columns) {
        if (columns == null || columns.length == 0) {
            throw new RuntimeException("Mybatis拦截器未获取到原SQL中的列名");
        }
        //这个map:当sql的返回值是Map类型时,需要将大写的key转成小写的key
        Map<String, String> columnsToMap1 = new HashMap<>(8);
        for (Column column : columns) {
            String name = column.name;
            //LABEL 是个特殊字,会默认大写,就算在sql中用的是小写
            if ("LABEL".equals(name)) {
                columnsToMap1.put(name.toUpperCase(), "label");

            } else {
                columnsToMap1.put(name.toUpperCase(), column.name);
            }
        }
        return columnsToMap1;
    }

    /**
     * 获取sql中查询结果中的列名
     *
     * @param invocation
     * @return
     */
    public static Column[] getColumns(Invocation invocation) throws SQLException {
        PreparedStatement statement = (PreparedStatement) invocation.getArgs()[0];
        DruidPooledResultSet generatedKeys = (DruidPooledResultSet) statement.getGeneratedKeys();
        DruidPooledStatement poolableStatement = generatedKeys.getPoolableStatement();
        PreparedStatementProxyImpl statement1 = (PreparedStatementProxyImpl) poolableStatement.getStatement();
        DmdbPreparedStatement rawObject = (DmdbPreparedStatement) statement1.getRawObject();
        return rawObject.columns;
    }
}

自己处理查询结果

这种是自己处理结果,不让框架去处理了


import com.alibaba.fastjson.JSON;
import org.apache.ibatis.executor.resultset.DefaultResultSetHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.ResultMapping;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.stereotype.Component;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class EntityIntercept {

    public static Object intercept(Invocation invocation) throws Throwable {
        List resList = new ArrayList();
        DefaultResultSetHandler defaultResultSetHandler = (DefaultResultSetHandler) invocation.getTarget();
        MetaObject metaStatementHandler = SystemMetaObject.forObject(defaultResultSetHandler);
        MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("mappedStatement");
        //获取节点属性的集合
        List<ResultMap> resultMaps = mappedStatement.getResultMaps();
        Class<?> resultType = resultMaps.get(0).getType();
        if (resultType == String.class || resultType == int.class) {
        //如果走这里,就会执行框架的invocation.proceed();方法处理返回结果
        } else {
        	//之前遇到Mybatis框架无法映射类型LocalDateTime,所以才有这个拦截器的存在,LocalDateTime具体解决方法看另外一篇
            //判断当前实体是否需要做LocalDateTime的转换
            if (!isLocalDate(resultMaps)) {
                return invocation.proceed();
            }
            //处理返回的实体中有LocalDateTime的
            Map<String, String> columnsAndFieldMap = columnsAndFieldMap(resultMaps);
            //获取mybatis返回的实体类类型名
            int resultMapCount = resultMaps.size();
            if (resultMapCount > 0) {
                Statement statement = (Statement) invocation.getArgs()[0];
                ResultSet resultSet = statement.getResultSet();
                if (resultSet != null) {
                    //获得对应列名
                    ResultSetMetaData rsmd = resultSet.getMetaData();
                    List<String> columnList = new ArrayList<>();
                    for (int i = 1; i <= rsmd.getColumnCount(); i++) {
                    //这里是拿到所有的列名,在达梦中应该都是大写的
                    //通过这些列名再去拿当前列的查询结果
                        columnList.add(rsmd.getColumnName(i));
                    }
                    while (resultSet.next()) {
                        Map resultMap = new HashMap<>();
                        for (String colName : columnList) {
                        //通过这些列名再去拿当前列的查询结果,resultSet.getString(colName)
                       		 resultMap.put(columnsAndFieldMap.get(colName), resultSet.getString(colName));
                        }
                        //最后将我们的结果转成一个实体类resultType
                        resList.add(JSON.parseObject(JSON.toJSONString(resultMap), resultType));
                    }
                    return resList;
                }
            }

        }

        return invocation.proceed();
    }

    /**
     * 当sql返回值类型是实体类时,判断字段中是否有LocalDateTime
     *
     * @param resultMaps
     * @return
     */
    public static Boolean isLocalDate(List<ResultMap> resultMaps) {
        //ResultMap中sql列和实体类字段的映射
        Map<String, String> columnsToMap1 = new HashMap<>(8);
        ResultMap resultMap1 = resultMaps.get(0);
        List<ResultMapping> resultMappings = resultMap1.getResultMappings();

        for (ResultMapping resultMapping : resultMappings) {
            Class<?> javaType = resultMapping.getJavaType();
            if (javaType == LocalDateTime.class || javaType == LocalDate.class) {
                return true;
            }
        }
        return false;
    }

    /**
     * 当sql返回值类型是实体类时,获取到自定的ResultMap中sql列和实体类字段的映射
     *
     * @param resultMaps
     * @return
     */
    public static Map<String, String> columnsAndFieldMap(List<ResultMap> resultMaps) {
        //ResultMap中sql列和实体类字段的映射
        Map<String, String> columnsToMap1 = new HashMap<>(8);
        ResultMap resultMap1 = resultMaps.get(0);
        List<ResultMapping> resultMappings = resultMap1.getResultMappings();

        for (ResultMapping resultMapping : resultMappings) {
            if (resultMapping.getJavaType() != List.class) {
                columnsToMap1.put(resultMapping.getColumn().toUpperCase(), resultMapping.getProperty());
            }
        }
        return columnsToMap1;
    }

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
### 回答1: MyBatis-Plus支持达梦数据库,可以通过下面的示例来实现对达梦数据库的适配:1. 在pom.xml文件中添加达梦数据库的驱动依赖: <dependency> <groupId>dm</groupId> <artifactId>dm-driver</artifactId> <version>7.7</version> </dependency>2. 在application.yml文件中配置达梦数据库的连接:spring: datasource: url: jdbc:dm://localhost:5236/test username: user password: 123456 driver-class-name: dm.jdbc.driver.DmDriver3. 在Mybatis-Plus的配置文件中添加对达梦数据库的自定义配置:mybatis-plus: configuration: dm: sql-injector: com.baomidou.mybatisplus.extension.injector.LogicSqlInjector4. 重新启动应用,MyBatis-Plus就可以正常使用达梦数据库了。 ### 回答2: MyBatis-Plus是一款优秀的持久层框架,它在MyBatis基础上进行了扩展和增强,提供了更为便捷的开发方式。 要适配达梦数据库,首先需要在项目的依赖管理中添加达梦数据库的驱动,可以通过Maven或Gradle下载并配置。 然后,在项目的配置文件中配置数据库连接信息,包括数据库类型、URL、用户名和密码等。 接下来,在项目中引入MyBatis-Plus的依赖,并配置相应的插件和拦截器,以支持MyBatis-Plus的特性和功能。可以使用@EnableMybatisPlus注解开启MyBatis-Plus的自动配置。 在编写数据访问层的代码时,可以使用MyBatis-Plus提供的通用Mapper接口和Wrapper查询构造器来实现便捷的数据库操作。通用Mapper接口提供了常见的增删改查方法,可以不再需要手动编写SQL语句,大大简化了开发工作。而Wrapper查询构造器提供了更为灵活的查询方式,可以进行复杂的条件查询。 此外,MyBatis-Plus还提供了代码生成器,可以根据数据库表结构自动生成实体类和Mapper接口的代码,极大地减少了手动创建的工作量。 综上所述,适配达梦数据库的示例主要包括添加数据库驱动、配置数据库连接信息、引入MyBatis-Plus依赖、配置插件和拦截器、使用通用Mapper接口和Wrapper查询构造器、以及使用代码生成器等步骤。通过这样的配置和操作,我们可以方便地使用MyBatis-Plus来操作达梦数据库,提高开发效率。 ### 回答3: MyBatis-Plus是一个在MyBatis基础之上进行扩展的数据库持久化框架,它提供了一系列的增强功能,简化了数据库操作的编写流程。但是,默认情况下,MyBatis-Plus并不支持达梦数据库,因为达梦数据库和其他主流数据库有一些不同之处。 要在MyBatis-Plus中适配达梦数据库,首先需要引入达梦数据库的相关的驱动包。可以下载达梦数据库官方提供的JDBC驱动包,并将其添加到项目的依赖中。 接着,在配置文件中,需要指定使用达梦数据库数据库方言。可以通过设置 `mybatis-plus.global-config.db-config.db-type` 属性来指定数据库方言为`dm`。 然后,需要配置数据源,指定连接达梦数据库的相关信息,比如数据库的URL、用户名和密码等。可以通过在配置文件中配置数据源相关的属性来完成这一步骤。 接下来,可以使用MyBatis-Plus提供的各种操作方法来进行数据库的增删改查等操作。MyBatis-Plus中的操作方法和MyBatis中的类似,只是在一些语法和使用上有一些不同,需要根据达梦数据库的特点进行相应的调整。 最后,根据实际需求进行一些其他的配置和优化。比如,可以通过配置文件中的属性来设置一些缓存策略、分页方言等。 总之,适配达梦数据库主要包括引入相关的驱动包、配置数据源、设置数据库方言和使用MyBatis-Plus的操作方法等步骤。通过这些步骤的完成,就可以在MyBatis-Plus中成功适配达梦数据库,并进行相应的数据库操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT界的老菜鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值