对于mybatis返回结果不需要用resultVO,使用map的一点自己的思考

问题描述

在我们使用mybatis开发的时候,像下面这样的代码大家都很熟悉吧

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lenovo.mfe.mapper.base.BaseDataAssetsMapper">
  <resultMap id="BaseResultMap" type="com.lenovo.mfe.po.DataAssets">
    <id column="id" jdbcType="INTEGER" property="id" />
    <result column="type" jdbcType="VARCHAR" property="type" />
    <result column="field_name" jdbcType="VARCHAR" property="fieldName" />
    <result column="maintained_rate" jdbcType="DOUBLE" property="maintainedRate" />
    <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
  </resultMap>
</mapper>

这样我们在查询的时候返回BaseResultMap,就会根据我们配置的返回字段和DataAssets字段的对应关系来注入值。那么问题就来了,比如我的一个select查询需要关联到五张表,而且返回字段分别来自于五张表。按照以前的开发按经验,我们通常有两种方法,

方法一:

增加表中字段与PO对象的映射,需要配置

 <result column="XXX" jdbcType="VARCHAR" property="XXX" />

和增加DataAssets中的字段,并且重新复写DataAssets的toString方法。(每个PO的toString都要去复写,方便我们在调试程序的时候把值都打印出来,不然打印出来的就是内存地址,这也是《Effective java》一书中对开发者的建议)。这样会污染PO。因为新加的字段可能跟这个PO对象一点关系都没有。

方法二:

新建一个VO,组装所有需要的字段,实现setter,getter,toString方法。增加一个<resultMap......</resultMap>的配置。这样不会污染原始的PO,但是我们这样的需求很多的话,会增加很多的VO和<resultMap…的配置。增加了项目的代码量。
可以看到,这样的改动是很麻烦的,而且我们在开发的时候大部分都会需要做联表操作。我的想法是,把所有的返回都定义为map,那么我们XML中的配置会是这样

   <select id="getPropertyByType" parameterType="String" resultType="java.util.HashMap">
    SELECT NAME,VALUE FROM property WHERE NAME = #{type} AND DATE_FORMAT(create_time,'%Y-%m') = #{currentMonth};
  </select>

mapper层:

List<Map<String, Object>> getPropertyByType(@Param("type") String type, @Param("currentMonth") String currentMonth);

那么这是我们就不需要专门存放数据的VO了,减少了代码量。但是这里有两个问题:

会出现的两个问题和解决方法

问题一:

就是,我们得到的变量是数据库中的字段名,这当然和我们的驼峰命名不一样,比如在数据库表中可能是,table_total_count,
我们需要的是tableTotalCount。我的方法是写一个format处理的方法。运用aop的思想去让所有满足条件的controller的方法都执行我的format方法,完美解决
format方法代码:

public static List<Map<String, Object>> toCamel(List<Map<String, Object>> list){
        List newlist = new ArrayList();
        for(Map<String, Object> map:list){
            newlist.add(formatMapKey(map));
        }

        return newlist;

    }

    public static Map formatMapKey(Map<String, Object> map){

        Map<String, Object> hashMap = new HashMap<>();

        for(Map.Entry<String, Object> entry:map.entrySet()){
            hashMap.put(CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, entry.getKey()), entry.getValue());

        }

        return hashMap;
    }

AOP配置:

@Aspect
@Component
public class ResultFormatAspect {

    private static final Logger logger = LoggerFactory.getLogger(ResultFormatAspect.class);

    @Pointcut("execution(public * com.XXX.mfe.controller.*.get*(..))")
    public void webLog(){}
    
    @AfterReturning(returning = "ret", pointcut = "webLog()")
    public void doAfterReturning(Object ret) throws Throwable {
        // 处理完请求,返回内容
        //JsonResult 是我定义的统一返回的格式。有三个字段String  code String  message T data
        JsonResult jsonResult = (JsonResult) ret;
        //System.out.println("方法的返回值处理前 : " + ret);
        jsonResult.setData(CommonUtil.toCamel((List<Map<String, Object>>) jsonResult.getData()));
        //System.out.println("方法的返回值处理后 : " + ret);
    }
}

处理之前的数据:
对于mybatis返回结果不需要用resultVO,使用map的一点自己的思考
处理后的数据:
对于mybatis返回结果不需要用resultVO,使用map的一点自己的思考

问题二:

如果值为null的变量。不会出现在返回结果里面。
在数据库配置setSqlSessionFactory的方法里添加一下三行代码

        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        configuration.setCallSettersOnNulls(true);
        bean.setConfiguration(configuration);

像这样:

@Bean(name = "baseSqlSessionFactory")
    @Primary
    public SqlSessionFactory setSqlSessionFactory(@Qualifier("baseDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        Interceptor[] plugins =  new Interceptor[]{pageHelper()};
        bean.setPlugins(plugins);
        //设置默认属性
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        configuration.setCallSettersOnNulls(true);
        bean.setConfiguration(configuration);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/base/*.xml"));
        return bean.getObject();
    }

为null的变量就会出现在我们的返回json串里面了。
对于mybatis返回结果不需要用resultVO,使用map的一点自己的思考
以上是我自己的一些小小的思考,有不足的地方也请各路大神批评指正,谢谢啦!