1.背景
本人自入職以來,接了一堆歷史包(shi)袱(shan),其中有個服務是基於MongoDB實現的,而公司要搞信創化,去掉MongoDB是遲早的事,於是便開始分析底層數據結構,計劃用pg替換MongoDB。
2.難點
2.1 數據結構不同
MongoDB是文件型資料庫,而pg是關係型資料庫,原專案MongoDB的collection中某些欄位型別為List或者entity,存在一對多的情況,如果採用新建關聯表的方式,會增加數據結構複雜性,且底層資料幾乎不會變,因此,覺得直接用VARCHAR型別儲存List型別的json串。
2.2 資料型別轉換
服務的資料庫持久化使用的是jpa,原來MongoDB型別會自動轉成List,如下列的options屬性
@Data @Document(collection = "input_item") public class InputItem implements Comparable<InputItem> { @Id private String itemCode; private String title; private String description; private int itemType; private List<InputItemOption> options; private String defaultOptionNum; private String unit; private boolean whetherActive; }
轉成pg後,options欄位在資料庫中是String型別,需要轉成List。經過調研,可以採用自定義Converter+註解@Convert來實現。
2.2.1 List型別
1)自定義Converter
import com.alibaba.fastjson.JSON; import javax.persistence.AttributeConverter; /** * @ClassName JpaConverterListJson * @Description jpa list轉換為String 相互轉換工具類 * @Author ygt * @Date 2021/3/3 14:49 * @Version V1.0 */ public class JpaConverterListJson implements AttributeConverter<Object, String> { @Override public String convertToDatabaseColumn(Object o) { return JSON.toJSONString(o); } @Override public Object convertToEntityAttribute(String s) { return JSON.parseArray(s); } }
2)@Convert註解
import java.util.List; import com.fasterxml.jackson.annotation.JsonInclude; import io.swagger.annotations.ApiModel; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.NoArgsConstructor; import lombok.Data; import javax.persistence.*; /** * * @Description 輸入項實體類 * */ @ApiModel("") @JsonInclude(value = JsonInclude.Include.NON_NULL) @Table(name = "input_item") @Builder @AllArgsConstructor @NoArgsConstructor @Data @Entity public class InputItem implements Comparable<InputItem> { @GeneratedValue @Column(name = "id") @Id private String itemCode; @Column(name = "title") private String title; @Column(name = "description") private String description; @Column(name = "itemtype") private int itemType; @Convert(converter = JpaConverterListJson.class) private List<InputItemOption> options; @Column(name = "defaultoptionnum") private String defaultOptionNum; @Column(name = "unit") private String unit; @Column(name = "whetheractive") private Boolean whetherActive; @Override public int compareTo(InputItem o) { int index1 = Integer.parseInt(this.itemCode.substring(this.itemCode.lastIndexOf('_') + 1)); int index2 = Integer.parseInt(o.itemCode.substring(o.itemCode.lastIndexOf('_') + 1)); if (index1 < index2) { return -1; } else { return 1; } } }
2.2.2 entity型別
1)自定義Converter
import com.alibaba.fastjson.JSON; import com.fasterxml.jackson.databind.ObjectMapper; import com.test.src.domain.index.interest.InterestCreditScoreSection; import javax.persistence.AttributeConverter; import java.lang.reflect.ParameterizedType; /** * @ClassName JpaConverterListJson * @Description jpa 泛型T轉換為String 相互轉換工具類 * @Author ygt * @Date 2021/3/3 14:49 * @Version V1.0 */ public class JpaConverterObjectJson<T> implements AttributeConverter<T, String> { private static final ObjectMapper objectMapper = new ObjectMapper(); @Override public String convertToDatabaseColumn(T o) { try { return objectMapper.writeValueAsString(o); } catch (Exception e) { throw new RuntimeException("Failed to convert object to string", e); } } @Override public T convertToEntityAttribute(String s) { try { return objectMapper.readValue(s, (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]); } catch (Exception e) { throw new RuntimeException("Failed to convert string to object", e); } } }
import com.fasterxml.jackson.annotation.JsonInclude; import com.test.src.utils.InterestCreditScoreSectionConverter; import io.swagger.annotations.ApiModel; import lombok.NoArgsConstructor; import lombok.*; import javax.persistence.*; /** * * @Description 利率授信實體類 */ @ApiModel("") @JsonInclude(value = JsonInclude.Include.NON_NULL) @Table(name = "interest_credit") @Builder @AllArgsConstructor @NoArgsConstructor @Data @Entity public class InterestCredit { @Id @GeneratedValue @Column(name = "id") private String itemNo; @Column(name = "recommendinterestformula") private String recommendInterestFormula; //推薦執行利率計算公式; @Convert(converter = InterestCreditScoreSectionConverter.class) @Column(name = "interestcreditscoresection") private InterestCreditScoreSection interestCreditScoreSection; }
2)@Convert註解
import com.fasterxml.jackson.annotation.JsonInclude; import com.test.src.utils.InterestCreditScoreSectionConverter; import io.swagger.annotations.ApiModel; import lombok.NoArgsConstructor; import lombok.*; import javax.persistence.*; /** * * @Description 利率授信實體類 */ @ApiModel("") @JsonInclude(value = JsonInclude.Include.NON_NULL) @Table(name = "interest_credit") @Builder @AllArgsConstructor @NoArgsConstructor @Data @Entity public class InterestCredit { @Id @GeneratedValue @Column(name = "id") private String itemNo; @Column(name = "recommendinterestformula") private String recommendInterestFormula; //推薦執行利率計算公式; @Convert(converter = InterestCreditScoreSectionConverter.class) @Column(name = "interestcreditscoresection") private InterestCreditScoreSection interestCreditScoreSection; }
3.總結
針對不好處理的json string型別,可透過自定義converter+@Convert的方式實現自動轉換,另外,可在自定義converter類上加@Convert(autoApply = true)實現全域性自動轉換。