对于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);
}
}
处理之前的数据:
处理后的数据:
问题二:
如果值为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串里面了。
以上是我自己的一些小小的思考,有不足的地方也请各路大神批评指正,谢谢啦!