周青的日常问题记录

项目场景

学习硅谷电商毕设项目_微服务版本,建站练手。进行到商城前端服务开发的热门商品分类展示服务 store-front-product阶段。

想实现的功能为根据类别名称(categoryName)查询出该类别下的热门商品列表(productList)。

对应数据库两张表(类别表与商品表):

go respone转换成json json转get_hash


go respone转换成json json转get_json_02


有问题的Impl类下逻辑代码如下:

@Service
@Slf4j
public class ProductServiceImpl implements ProductService {

    @Autowired
    private CategoryClient categoryClient;

    @Autowired
    private ProductMapper productMapper;

    /**
     * 1.根据单类别名称,调用feign,访问类别服务获取类别数据
     * 2.成功 继续根据类别id查询商品数据 【热门 销售量倒序 查询前7个】
     * 3.封装结果
     * @param categoryName
     * @return
     */
    @Override
    public R promo(String categoryName) {

        R r = categoryClient.byName(categoryName);

        if(r.getCode().equals(R.FAIL_CODE)){
            log.info("ProductServiceImpl业务结束,结果:{}","类别查询失败");
            return r;
        }

        Category category = (Category) r.getData();
        Integer categoryId = category.getCategoryId();

        //封装查询参数
        QueryWrapper<Product> proqueryWrapper = new QueryWrapper<>();
        proqueryWrapper.eq("category_id",categoryId);
        proqueryWrapper.orderByDesc("product_sales");

        //分页
        IPage<Product> page = new Page<>(1,7);

        page = productMapper.selectPage(page,proqueryWrapper);

        List<Product> productList = page.getRecords();
        long total = page.getTotal();
        log.info(String.valueOf(total));

        log.info("ProductServiceImpl.promo业务结束,结果:{}",productList);
        return R.ok("数据查询成功(product)",productList);
    }
}

R实体类(部分)

@Data

public class R {
    /**
     * 通用成功状态码
     */
    public static final String SUCCESS_CODE = "001";
    /**
     * 失败状态码
     */
    public static final String FAIL_CODE = "004";

    private String code;
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private String msg;
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private Object data;
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private Long   total;


    public static R ok(String msg,Object data){
        return ok(msg,data,null);
    }

问题描述

运行后报错如下:
Java.lang.ClassCastException: java,util.LinkedHashMap cannot be cast to com,atguigu,pojo.Category 根据老师解释,通过feign网络请求将查询到的categoryId发送到product服务,json在传递到Java中Jackson默认将json转换成LInkHashMap类型。老师的解决方法是不再用自定义类Category而是用LInkHashMap类来getData,把之前的

Category category = (Category) r.getData();
        Integer categoryId = category.getCategoryId();

换成

LinkedHashMap<String,Object> map = (LinkedHashMap<String, Object>) r.getData();
	Integer categoryId = (Integer) map.get("categoryId");

后老师成功运行。
问题来了
问题来了
问题来了
我跟着做此改动后,虽然代码成功运行,后台没有报错,但postman输出的data中为空,没有查询到任何数据


原因分析

经测试排查,数据库没有问题,r.getData()可以正常查询到根据categoryName所查到的category表中元素,但接下来的categoryId却取不到。
原因可能在于,使用LInkHashMap接收Data后,再对LInkHashMap类型的map用get方法,因LInkHashMap的get方法需要判断传入key的哈希值相等才能返回get结果,所以我用同样的文本“categoryId”查不到我想要的结果。

(此处感叹:学面试八股有用啊)

附源码:

public V get(Object key)
{
	Node<K,V> e;
	//调用核心方法getNode来获取对应值
	return (e = getNode(hash(key), key)) == null ? null : e.value;
}
static final int hash(Object key)
{
    int h;
    //1.如果key为null则返回0 
    //2.根据key的hashCode值逻辑右移16做散列处理得到哈希值
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
//传入参数:1.根据key散列计算得到的哈希值 2.key值
final Node<K,V> getNode(int hash, Object key)
{
	Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
	//判断表是否为空,判断key对应的链表节点是否存在
	if ((tab = table) != null && (n = tab.length) > 0 &&  (first = tab[(n - 1) & hash]) != null) 
	{
	   //判断链表的头部元素是否是key值对应的真实value,对比项:1.hash 2.key值
	   if (first.hash == hash && ((k = first.key) == key || (key != null && key.equals(k))))
	       return first;
	   //判断链表下个元素是否存在,存在则继续往下走
	   if ((e = first.next) != null)
	   {
	     //判断是否是红黑树
	     if (first instanceof TreeNode)
	     //走红黑树逻辑,从红黑树中获取对应的value
	    	 return ((TreeNode<K,V>)first).getTreeNode(hash, key);
	     //遍历链表
	     do 
	     {
	        //判断链表下个元素是否是key值对应的真实value,对比项:1.hash 2.key值
	        if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
	             return e;
	        } while ((e = e.next) != null);
	            
	   	}
	  }
	  //没有找到对应value则返回null
	  return null;
}

解决方案

使用com.alibaba.fastjson这一工具包帮助我们进行java对象和json格式的字符串之间的相互转换,曲线救国。

  1. 在对应服务的pom中导入依赖
< dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.47</version>
< /dependency>
  1. 更改impl实现类代码
LinkedHashMap<String,Object> map = (LinkedHashMap<String, Object>) r.getData();
	Integer categoryId = (Integer) map.get("categoryId");

改为

LinkedHashMap<String,Object> map = (LinkedHashMap<String, Object>) r.getData();
    //将对象转换为字符串
    String jsonmap = JSON.toJSONString(map);
    //将json格式字符串转换为自定义类
    Category category = (Category)JSON.parseObject(jsonmap,Category.class) ;
    Integer categoryId = category.getCategoryId();
  1. install对应的product服务包,rerun服务。
  2. 测试通过,前端联调正常显示数据图片。

后记

在意识到hashmap的问题导致自己查不出数据后,曾尝试过重写hashcode()方法和equal()方法,如此尝试后并没有成功。如果有朋友改hash成功的话欢迎留言讨论。

参考文章
[1] RookieJay.关于map的细节-map.get(Object key)为null
[2] 十_亿_光_年.LinkedHashMap 不能强转为自定义的对象的解决方案
[3] 尽力漂亮. com.alibaba的fastjson简介