环境:
使用spring mvc 配置json消息转换器为MappingJackson2HttpMessageConverter
发现long类型的数据到了js端会丢失精度
解决方案:
将long统一转为string类型
方法一:
数据层转换,由于项目使用了spring 的jdbc模版类,查询时调用了spring的query方法
public <T> List<T> query(String sql, SqlParameterSource paramSource, RowMapper<T> rowMapper) throws DataAccessException { return getJdbcOperations().query(getPreparedStatementCreator(sql, paramSource), rowMapper); }
这个方法有个参数rowMapper,我用来将数据库数据转换为Map,我通过自定义实现RowMapper接口,将mysql数据库里的bigint类型转为string而不是默认的long,代码如下:
public class HashMapRowMapper implements RowMapper<Map<String, Object>> { @Override public Map<String, Object> mapRow(ResultSet rs, int rowNum) throws SQLException { ResultSetMetaData rsmd = rs.getMetaData(); int columnCount = rsmd.getColumnCount(); Map<String, Object> mapOfColValues = new LinkedCaseInsensitiveMap<Object>(columnCount); for (int i = 1; i <= columnCount; i++) { String key = JdbcUtils.lookupColumnName(rsmd, i); Object obj = JdbcUtils.getResultSetValue(rs, i); if (obj == null) { mapOfColValues.put(key, obj); } if (obj != null) { Class<?> cc = obj.getClass(); if (cc.getName().equals("java.math.BigInteger") && obj != null) { mapOfColValues.put(key, String.valueOf(obj)); } else { mapOfColValues.put(key, obj); } } } return mapOfColValues; } }
调用代码如下,注意query传入的最后一个参数使用了自定已类HashMapRowMapper:
public Map<String, Object> getItem(Map<String, Object> inParam) { if (MapUtil.getValue("id", inParam) == "") throw new InParamCheckException("id不能为空:" + inParam.get("id")); String orgId = MapUtil.getValue("org_id", inParam); String appId = IDConstants.APP_ID_PIS; NamedParameterJdbcTemplate jdbc = jdbcRouteHelper.getJDBCTemplate(orgId, appId); return new MyMap().put("simple_info", jdbc.query("select * from simple_info where id=:id", inParam, new HashMapRowMapper())) .put("simple_test", jdbc.query("select * from simple_test where simple_info_id=:id", inParam, new HashMapRowMapper())) .getMap(); }
方法二:
配置spring的消息转换器的ObjectMapper为自定义的类
配置如下:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="messageConverters"> <list> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="objectMapper"> <bean class="LongToStringJsonConverter"> <property name="supportedMediaTypes"> <list> <value>application/json;charset=UTF-8</value> <value>text/html;charset=UTF-8</value> <value>text/plain;charset=UTF-8</value> </list> </property> <property name="dateFormat"> <bean class="java.text.SimpleDateFormat"> <constructor-arg type="java.lang.String" value="yyyy-MM-dd HH:mm:ss" /> </bean> </property> </bean> </property> </bean> </bean>
其中LongToStringJsonConverter为自定义转换器
public class LongToStringJsonConverter extends ObjectMapper { /** * */ private static final long serialVersionUID = 1683531771040674386L; @Override public ObjectMapper registerModule(Module module) { SimpleModule simpleModule = new SimpleModule(); simpleModule.addSerializer(Long.class, ToStringSerializer.instance); simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance); return super.registerModule(simpleModule); } }
spring中controller层的代码使用时无需特别修改,下面是一个controller类里返回json数据,代码不做任何修改,long类型已经转为string返回到web浏览器了.
@RequestMapping("/") @ResponseBody public Map<String, Object> init() { List<Object> lstDoctor = dictConsumer.getBaseParam().getItemsFromCache(cache_keys.PIS_doctor.name(), test_org_id, IDConstants.APP_ID_PIS); List<Object> lstSubjectType = dictConsumer.getBaseParam().getItemsFromCache(cache_keys.PIS_subject_type.name(), test_org_id, IDConstants.APP_ID_PIS); List<Object> lstTest = dictConsumer.getBaseParam().getItemsFromCache(cache_keys.PIS_test.name(), test_org_id, IDConstants.APP_ID_PIS); MyMap map = new MyMap(); map.put("doctors", lstDoctor).put("subject_types", lstSubjectType).put("tests", lstTest); return map.getMap(); }