SprintBoot系列(十三):JPA的Json与List映射

回忆一下,之前我们访问的spuList时候,返回的sku的json格式,并不能很好地被前端调用,我们需要进行改进。

JAVA、基础技术、技术与框架SprintBoot系列(十三):JPA的Json与List映射插图

这里的json格式分为单体json和数组型json。如何通过优化让前端更好调用呢?

  • 单体Json对应的是map<string,Object>
  • 数组型Json对应的是List<Map(string,Object)>或者List<类>

方法一:创建MapAndJson和ListAndJson并利用注解方式序列化

1、单体Json与Mpa的映射

1.1、在util中,创建一个MapAndJson的类,用于处理单体Json与Map的处理

package com.fcors.fcors.util;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fcors.fcors.exception.ServerErrorException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import java.util.HashMap;
import java.util.Map;

@Converter
public class MapAndJson implements AttributeConverter<Map<String, Object>, String> {
    @Autowired
    private ObjectMapper mapper;

    @Override
    public String convertToDatabaseColumn(Map<String, Object> stringObjectMap) {
        
        try {
            return mapper.writeValueAsString(stringObjectMap);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServerErrorException(9999);
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public Map<String, Object> convertToEntityAttribute(String s) {

        Map<String,Object> t = new HashMap<String,Object>();
        if(s==null){
            return t;
        }
        try {
             t = mapper.readValue(s, HashMap.class);
            return t;
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServerErrorException(9999);
        }
    }
}

1.2、创建一个ServerErrorException

在execption下面创建一个ServerErrorException类

package com.fcors.fcors.util;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fcors.fcors.exception.ServerErrorException;
import org.springframework.beans.factory.annotation.Autowired;

import javax.persistence.AttributeConverter;
import java.util.HashMap;
import java.util.Map;

public class MapAndJson implements AttributeConverter<Map<String, Object>, String> {
    @Autowired
    private ObjectMapper mapper;

    @Override
    public String convertToDatabaseColumn(Map<String, Object> stringObjectMap) {
        try {
            return mapper.writeValueAsString(stringObjectMap);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServerErrorException(9999);
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public Map<String, Object> convertToEntityAttribute(String s) {
        try {
            Map<String, Object> t = mapper.readValue(s, HashMap.class);
            return t;
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServerErrorException(9999);
        }
    }
}

1.3、添加错误码

在recources/config的exception-code.properties追加错误码

fcors.codes[9999]=服务器未知错误异常
JAVA、基础技术、技术与框架SprintBoot系列(十三):JPA的Json与List映射插图1

1.4、修改Sku的model

package com.fcors.fcors.model;

import com.fcors.fcors.util.MapAndJson;
import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.Map;
import java.util.Objects;

@Entity
@Getter
@Setter
@Table(name = "sku")
public class SkuEntity extends BaseEntity{
    @Id
    @Column(name = "id")
    private Long id;
    private BigDecimal price;
    private BigDecimal discountPrice;
    private byte online;
    private String img;
    private String title;
    private Long spuId;
    private String specs;
    private String code;
    private Long stock;
    private Long categoryId;
    private Long rootCategoryId;

//    private String test;
    /*把原来的String 改成 Map */
    @Convert(converter= MapAndJson.class)
    private Map<String,Object> test;

}
JAVA、基础技术、技术与框架SprintBoot系列(十三):JPA的Json与List映射插图2

下面开始讲解数组型JSON与List的映射

2、数组型JSON与List的映射

1、在util下面创建ListAndJson类

package com.fcors.fcors.util;


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fcors.fcors.exception.ServerErrorException;
import org.springframework.beans.factory.annotation.Autowired;

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import java.util.List;

@Converter
public class ListAndJson implements AttributeConverter<List<Object>, String> {
    @Autowired
    private ObjectMapper mapper;

    @Override
    public String convertToDatabaseColumn(List<Object> objects) {
        try {
            return mapper.writeValueAsString(objects);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            throw new ServerErrorException(9999);
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<Object> convertToEntityAttribute(String s) {
        try {
            if(s == null){
                return null;
            }
            List<Object> t = mapper.readValue(s, List.class);
            return t;
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServerErrorException(9999);
        }
    }
}

2.2 在model下创建一个Spec的类

List的话需要创建类,不然因为类型问题容易引起后面的错误。

因为没有数据表,所以不需要加入@Entity

package com.fcors.fcors.model;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.Setter;

import javax.persistence.Entity;
import javax.persistence.Table;

@Getter
@Setter
public class Spec {
    @JsonProperty("keyId")
    private Long key_id;
    private String key;
    @JsonProperty("valueId")
    private Long value_id;
    private String value;
}

2.3 修改Model下面的Sku

package com.fcors.fcors.model;

import com.fcors.fcors.util.ListAndJson;
import com.fcors.fcors.util.MapAndJson;
import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

@Entity
@Getter
@Setter
@Table(name = "sku")
public class SkuEntity extends BaseEntity{
    @Id
    @Column(name = "id")
    private Long id;
    private BigDecimal price;
    private BigDecimal discountPrice;
    private byte online;
    private String img;
    private String title;
    private Long spuId;

    /*把原来的String改成下面的*/
    @Convert(converter = ListAndJson.class)
    private List<Spec> specs;
//    private String specs;
    private String code;
    private Long stock;
    private Long categoryId;
    private Long rootCategoryId;

//    private String test;
    @Convert(converter= MapAndJson.class)
    private Map<String,Object> test;
}
JAVA、基础技术、技术与框架SprintBoot系列(十三):JPA的Json与List映射插图3

完成序列化。

方法二:创建GenericAndJson公共方法类

1、在util处新增GenericAndJson类

package com.fcors.fcors.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fcors.fcors.exception.ServerErrorException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class GenericAndJson {
    private static ObjectMapper mapper;

    @Autowired
    public void setMapper(ObjectMapper mapper) {
        GenericAndJson.mapper = mapper;
    }

    public static <T> String objectToJson(T o) {
        String resp=null;
        if(o ==null){
            return null;
        }
        try {
            resp=(String) (o.equals(String.class)?o:mapper.writerWithDefaultPrettyPrinter().writeValueAsString(o));
            return resp;
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServerErrorException(9999);
        }
    }


    public static <T> T jsonToObject(String s,  TypeReference<T> tr) {
        if (s == null) {
            return null;
        }
        try {
            T o = (T)GenericAndJson.mapper.readValue(s, tr);
            return o;
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            throw new ServerErrorException(9999);
        }
    }
}

2、修改model下的Sku类的代码

将单体Json或数组型Json的类型设置成String,然后重新设置Getter和Setter方法

package com.fcors.fcors.model;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import com.fcors.fcors.util.GenericAndJson;
import com.fcors.fcors.util.ListAndJson;
import com.fcors.fcors.util.MapAndJson;
import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
import java.math.BigDecimal;
import java.util.*;

@Entity
@Getter
@Setter
@Table(name = "sku")
public class SkuEntity extends BaseEntity{
    @Id
    @Column(name = "id")
    private Long id;
    private BigDecimal price;
    private BigDecimal discountPrice;
    private byte online;
    private String img;
    private String title;
    private Long spuId;


    private String code;
    private Long stock;
    private Long categoryId;



    private Long rootCategoryId;

    /**方法一:数组型Json注解方式**/
//    @Convert(converter = ListAndJson.class)
//    private List<Spec> specs;

    /**方法二:数组型JSON-GenericAndJson **/

    private String specs;
    public List<Map<String,Object>> getSpecs() {
        List<Map<String,Object>> list = new ArrayList<Map<String,Object>>();
        if (specs == null) return list;
        list = GenericAndJson.jsonToObject(this.specs, new TypeReference<List<Map<String,Object>>>(){});
        return list;
    }
    public void setSpecs(List<Spec> specs) {
        if (specs.isEmpty()) return;
        this.specs = GenericAndJson.objectToJson(specs);
    }





    /*方法一:单体Json注解方式*/
//    @Convert(converter= MapAndJson.class)
//    private Map<String,Object> test;

    /** 方法二:单体JSON-GenericAndJson */
    private String test;
    public Map<String,Object> getTest() {
        Map<String,Object> spec = new HashMap<String,Object>();;
        if(this.test ==null){
            return spec;
        }
        spec = GenericAndJson.jsonToObject(this.test, new TypeReference<HashMap<String, Object>>() {
        });
        return spec;

    }

    public void setTest(Spec test) {
        if(test==null){
            return;
        }
        this.test = GenericAndJson.objectToJson(test);
    }

}

注意:如果没有使用Vo层的话,接口返回Json是不会报错的,但如果使用了Vo层需要Vo层的实体进行修改。

JAVA、基础技术、技术与框架SprintBoot系列(十三):JPA的Json与List映射插图4

如图使用了Vo层过滤数据,我们需要在vo层重新定义一个“SkuEntityDTO”

JAVA、基础技术、技术与框架SprintBoot系列(十三):JPA的Json与List映射插图5
JAVA、基础技术、技术与框架SprintBoot系列(十三):JPA的Json与List映射插图6

切记添加上@Getter和@Setter

JAVA、基础技术、技术与框架SprintBoot系列(十三):JPA的Json与List映射插图7

方法二完成。

方法三:实现AttributeConverter

1.1、在util下面创建一个SuperGenericAndJson类

package com.fcors.fcors.util;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fcors.fcors.exception.ServerErrorException;
import org.springframework.beans.factory.annotation.Autowired;

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;

@Converter
public class SuperGenericAndJson<T> implements AttributeConverter<T, String> {
    @Autowired
    private ObjectMapper mapper;


    @Override
    public String convertToDatabaseColumn(T t) {
        if(t ==null){
            return null;
        }
        try {
            return mapper.writeValueAsString(t);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServerErrorException(9999);
        }
    }

    @Override
    public T convertToEntityAttribute(String s) {
        try {
            if (s == null) {
                return null;
            }

            T t = mapper.readValue(s, new TypeReference<T>() {
            });
            return t;
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServerErrorException(9999);
        }
    }
}

1.2、修改Model下面的Sku实体类

package com.fcors.fcors.model;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import com.fcors.fcors.util.GenericAndJson;
import com.fcors.fcors.util.ListAndJson;
import com.fcors.fcors.util.MapAndJson;
import com.fcors.fcors.util.SuperGenericAndJson;
import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
import java.math.BigDecimal;
import java.util.*;

@Entity
@Getter
@Setter
@Table(name = "sku")
public class SkuEntity extends BaseEntity{
    @Id
    @Column(name = "id")
    private Long id;
    private BigDecimal price;
    private BigDecimal discountPrice;
    private byte online;
    private String img;
    private String title;
    private Long spuId;


    private String code;
    private Long stock;
    private Long categoryId;



    private Long rootCategoryId;

    /**方法一:数组型Json注解方式**/
//    @Convert(converter = ListAndJson.class)
//    private List<Spec> specs;

    /**方法二:数组型JSON-GenericAndJson **/

//    private String specs;
//    public List<Map<String,Object>> getSpecs() {
//        List<Map<String,Object>> list = new ArrayList<Map<String,Object>>();
//        if (specs == null) return list;
//        list = GenericAndJson.jsonToObject(this.specs, new TypeReference<List<Map<String,Object>>>(){});
//        return list;
//    }
//    public void setSpecs(List<Spec> specs) {
//        if (specs.isEmpty()) return;
//        this.specs = GenericAndJson.objectToJson(specs);
//    }

    /**方法三:数组型JSON-SuperGenericAndJson **/
    @Convert(converter = SuperGenericAndJson.class)
    private List<Spec> specs;

    /**方法一:单体Json注解方式**/
//    @Convert(converter= MapAndJson.class)
//    private Map<String,Object> test;

    /** 方法二:单体JSON-GenericAndJson */
//    private String test;
//    public Map<String,Object> getTest() {
//        Map<String,Object> spec = new HashMap<String,Object>();;
//        if(this.test ==null){
//            return spec;
//        }
//        spec = GenericAndJson.jsonToObject(this.test, new TypeReference<HashMap<String, Object>>() {
//        });
//        return spec;
//
//    }
//
//    public void setTest(Spec test) {
//        if(test==null){
//            return;
//        }
//        this.test = GenericAndJson.objectToJson(test);
//    }

    /**方法三:单体JSON-SuperGenericAndJson **/
    @Convert(converter = SuperGenericAndJson.class)
    private Map<String,Object> test;

}

方法三较方法二相比,即便使用了Vo层,也不需要创建Dto层。

扩展学习:如何在model下添加条件筛选

使用@Where注解

@Where(clause = "delete_time is null ")

完整代码

package com.fcors.fcors.model;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import com.fcors.fcors.util.GenericAndJson;
import com.fcors.fcors.util.ListAndJson;
import com.fcors.fcors.util.MapAndJson;
import com.fcors.fcors.util.SuperGenericAndJson;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.Where;

import javax.persistence.*;
import java.math.BigDecimal;
import java.util.*;

@Entity
@Getter
@Setter
@Table(name = "sku")
@Where(clause = "delete_time is null ")
public class SkuEntity extends BaseEntity{
    @Id
    @Column(name = "id")
    private Long id;
    private BigDecimal price;
    private BigDecimal discountPrice;
    private byte online;
    private String img;
    private String title;
    private Long spuId;


    private String code;
    private Long stock;
    private Long categoryId;



    private Long rootCategoryId;

    /**方法一:数组型Json注解方式**/
//    @Convert(converter = ListAndJson.class)
//    private List<Spec> specs;

    /**方法二:数组型JSON-GenericAndJson **/

//    private String specs;
//    public List<Map<String,Object>> getSpecs() {
//        List<Map<String,Object>> list = new ArrayList<Map<String,Object>>();
//        if (specs == null) return list;
//        list = GenericAndJson.jsonToObject(this.specs, new TypeReference<List<Map<String,Object>>>(){});
//        return list;
//    }
//    public void setSpecs(List<Spec> specs) {
//        if (specs.isEmpty()) return;
//        this.specs = GenericAndJson.objectToJson(specs);
//    }

    /**方法三:数组型JSON-SuperGenericAndJson **/
    @Convert(converter = SuperGenericAndJson.class)
    private List<Spec> specs;

    /**方法一:单体Json注解方式**/
//    @Convert(converter= MapAndJson.class)
//    private Map<String,Object> test;

    /** 方法二:单体JSON-GenericAndJson */
//    private String test;
//    public Map<String,Object> getTest() {
//        Map<String,Object> spec = new HashMap<String,Object>();;
//        if(this.test ==null){
//            return spec;
//        }
//        spec = GenericAndJson.jsonToObject(this.test, new TypeReference<HashMap<String, Object>>() {
//        });
//        return spec;
//
//    }
//
//    public void setTest(Spec test) {
//        if(test==null){
//            return;
//        }
//        this.test = GenericAndJson.objectToJson(test);
//    }

    /**方法三:单体JSON-SuperGenericAndJson **/
    @Convert(converter = SuperGenericAndJson.class)
    private Map<String,Object> test;

}