日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring data JPA 之 Jackson 在实体里面的注解详解

發(fā)布時(shí)間:2024/3/24 javascript 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring data JPA 之 Jackson 在实体里面的注解详解 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

8 Spring data JPA 之 Jackson 在實(shí)體里面的注解詳解

經(jīng)過前?課時(shí)的講解,相信你已經(jīng)對實(shí)體??的 JPA 注解有了?定的了解,但是實(shí)際?作中你會(huì)發(fā)現(xiàn)實(shí)體??不僅有 JPA 的注解,也會(huì)?到很多 JSON 相關(guān)的注解。

我們? Spring Boot ??默認(rèn)集成的 fasterxml.jackson 加以說明,這看似和 JPA 沒什么關(guān)系,但是?旦我們和 @Entity ?起使?的時(shí)候,就會(huì)遇到?些問題,特別是新?同學(xué),我們這?課時(shí)詳細(xì)介紹?下?法。先來跟著我了解?下 Jackson 的基本語法。

8.1 Jackson 的基本語法

當(dāng)我們? spring boot starter 的時(shí)候就會(huì)默認(rèn)加載 fasterxml 相關(guān)的 jar 包模塊,包括核?模塊以及 jackson 提供的?些擴(kuò)展 jar 包,下?詳細(xì)介紹。

8.1.1 三個(gè)核心模塊

  • jackson-core:核?包,提供基于“流模式”解析的相關(guān) API,它包括 JsonPaser 和 JsonGenerator。Jackson 內(nèi)部實(shí)現(xiàn)正是通過?性能的流模式 API 的 JsonGenerator 和 JsonParser 來?成和解析 json。

  • jackson-annotations:注解包,提供標(biāo)準(zhǔn)注解功能,這是我們必須要掌握的基礎(chǔ)語法。

  • jackson-databind:數(shù)據(jù)綁定包,提供基于“對象綁定”解析的相關(guān) API(ObjectMapper) 和“樹模型”解析的相關(guān) API(JsonNode);基于“對象綁定”解析的API 和“樹模型”解析的 API 依賴基于“流模式”解析的 API。如下圖中?些標(biāo)準(zhǔn)的類型轉(zhuǎn)換:

  • 8.1.2 Jackson 提供的擴(kuò)展 jar 包

  • jackson-module-parameter-names:對原來的 jackson 進(jìn)?了擴(kuò)展,?持了構(gòu)造?法和?法基本的參數(shù)?持。
  • jackson-datatype:是對字段類型的?持做的?些擴(kuò)展,包括下述?個(gè)部分。
    • jackson-datatype-jdk8:是對 jdk8 語法??的?些 Optional、Stream 等?些新的類型做的?些?持。
    • jackson-datatype-jsr310:是對 jdk8 中的 JSR310 時(shí)間協(xié)議做了?持,如 Duration、Instant、LocalDate、Clock 等時(shí)間類型的序列化、反序列化。
    • .jackson-datatype-hibernate5:是對Hibernate的??的?些數(shù)據(jù)類型的序列化、反序列化,如 HibernateProxy 等。
  • 剩下不常?的咱們就不說了,jackson-datatype 其實(shí)就是對?些常?的數(shù)據(jù)類型做序列化、反序列化,省去了我們??寫序列化、反序列化的過程。所以在我們?作中,如果需要?定義序列化的時(shí)候,可以參考這些源碼。

    知道了這些脈絡(luò)之后,剩下的就是我們要掌握的注解有哪些了,下?我來介紹?下。

    8.1.3 Jackson 中常用的一些注解

    正如上?所說,我們打開 jackson-annotations,就可以看到有哪些注解了,??了然,閑著沒事的時(shí)候就可以到這??看看,這樣你會(huì)越來越熟悉。下?我們挑選?些常?的介紹?下。

    Jackson ??常?的注解如下所示:

    注解示例
    @JsonProperty用于屬性,把屬性的名稱序列化給 JSON 字符串時(shí)轉(zhuǎn)換為另外一個(gè)名稱
    @JsonProperty
    private String userName
    @JsonFormat用于屬性或者方法,把屬性的格式序列化時(shí)指定成指定的格式
    @JsonFormat
    private Date getCreateDate()
    @JsonPropertyOrder用于類,指定屬性在序列化時(shí) JSON 中的順序
    @JsonPropertyOrder({“birth_date”,“name”})
    private class User
    @JsonCreator用于構(gòu)造方法,和 @JsonProperty 配合使用,適用有參數(shù)的構(gòu)造方法
    @JsonCreator
    private User(@JsonProperty(“name”)String name) {}
    @JsonAnySetter用于屬性或者方法,設(shè)置未反序列化的屬性名和值作為鍵值存儲(chǔ)到 map 中
    @JsonAnySetter
    private void set(String key, Object value) {
    map.put(key, value);
    }
    @JsonAnyGetter用于方法,獲取所有未序列化的屬性,一般與 @JsonAnySetter 成對出現(xiàn)
    @JsonAnyGetter
    private Map<String,Object> get() {
    return map;
    }
    @JsonIgnore用于告訴 Jackson 在序列化,反序列化時(shí)忽略 Java 對象的某個(gè)屬性(字段)
    @JsonIgnore
    private long personId = 0;
    @JsonIgnoreProperties注解在類聲明上方,用于指定要忽略的類的屬性列表
    @JsonIgnoreProperties({“birth_date”,“name”})
    private class User
    @JsonAutoDetect用于告訴 Jackson 在讀寫對象時(shí),包括非 public 修飾的屬性
    @JsonDeserialize,@JsonSerialize用戶指定字段的自定義序列化、反序列化
    @JsonInclude用于告訴 Jackson 包括那些情況下的屬性,例如,僅僅顯示非空的字段
    @JsonInclude(JsonInclude.Include.NON_EMPTY)

    8.1.4 實(shí)例

    ?先,新建?個(gè) UserJson 實(shí)體對象,將它轉(zhuǎn)成 Json 對象,如下所示:

    @Entity @Data @Builder @AllArgsConstructor @NoArgsConstructor @JsonPropertyOrder({"createDate", "email"}) @JsonIgnoreProperties({"hibernateLazyInitializer"}) public class UserJson implements Serializable {@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;@JsonProperty("my_name")private String name;private Instant createDate;@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm")private Date updateDate;private String email;@JsonIgnoreprivate String sex;@JsonCreatorpublic UserJson(@JsonProperty("email") String email) {System.out.println("其他業(yè)務(wù)邏輯");this.email = email;}@Transient@JsonAnySetterprivate Map<String, Object> other = new HashMap<>();@JsonAnyGetterpublic Map<String, Object> getOther() {return other;} }

    然后,我們寫?個(gè)測試?例,看?下運(yùn)?結(jié)果。

    @DataJpaTest @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class UserJsonRepositoryTest {@Autowiredprivate UserJsonRepository userJsonRepository;@BeforeAll@Rollback(false)@Transactionalvoid init() {UserJson user = UserJson.builder().name("jackxx").createDate(Instant.now()).updateDate(new Date()).sex("men").email("123456@126.com").build();userJsonRepository.saveAndFlush(user);}@Test@Rollback(false)public void testUserJson() throws JsonProcessingException {UserJson userJson = userJsonRepository.getById(1L);userJson.setOther(Maps.newHashMap("address", "shanghai"));ObjectMapper objectMapper = new ObjectMapper();objectMapper.registerModule(new JavaTimeModule());System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(userJson));} }

    最后,運(yùn)??下可以看到如下結(jié)果。

    {"createDate" : 1646107023.872000000,"email" : "123456@126.com","id" : 1,"updateDate" : "2022-03-01 11:57","my_name" : "jackxx","address" : "shanghai" }

    我們通過例?可以很容易想到使?場景是 SpringMvc 的情況下,在 get 請求的時(shí)候我們要?到序列化;在 post 請求的時(shí)候我們要?到反序列化,將 json 字符串反向轉(zhuǎn)化成實(shí)體對象。

    那么在 Spring ?? Jackson 都有哪些應(yīng)?場景呢?我們來看?下。

    8.2 Jackson 和 Spring 的關(guān)系

    8.2.1 應(yīng)用場景一:Spring MVC 的 View 層

    在Spring MVC中,我們需要知道Mvc的JSON視圖的加載原理。我們看?下源碼,mvc 對象的轉(zhuǎn)化類:HttpMessageConvertersAutoConfiguration,??要利?JacksonHttpMessageConvertersConfiguration,如下所示:

    ???的 MappingJackson2HttpMessageConverter 正是采? fasterxml.jackson 進(jìn)?轉(zhuǎn)化的,看下?的圖?。

    8.2.2 應(yīng)用場景二:Open-Feign

    我們在微服務(wù)之間相互調(diào)?的時(shí)候,都會(huì)?到 HttpMessageConverter ??的JacksonHttpMessageConverter 進(jìn)?轉(zhuǎn)化。特別是在? open-feign ??的 Encode 和 Decode 的時(shí)候,我們就可以看到如下應(yīng)?場景:

    8.2.3 應(yīng)用場景三:Redis 里面

    redis、cacheable 都會(huì)?到 value 的序列化,都離不開 JSON 的序列化。

    8.2.4 應(yīng)用場景四:JMS 消息序列化

    當(dāng)我們項(xiàng)?之間解耦?到消息隊(duì)列的時(shí)候,可能會(huì)基于 JMS 消息協(xié)議發(fā)送消息,其也是基于 JSON 的序列化機(jī)制來繼續(xù) converter 的,它在? JmsTemplate 的時(shí)候也會(huì)遇到同樣情況。

    綜上四個(gè)場景所述,我們是經(jīng)常和 Entity 打交道的,? @Entity ?要在各種場景轉(zhuǎn)化成 JSONString,所以 Jackson 的原理我們還是要掌握?些的,下?來分析?個(gè)?較重要的。

    8.3 Jackson 的原理分析

    8.3.1 Jackson 的可見性原理分析

    前?我們看到了注解 @JsonAutoDetect JsonAutoDetect.Visibility 類包含與 Java 中的可?性級(jí)別匹配的常量,表示 ANY、DEFAULT、NON_PRIVATE、NONE、PROTECTED_AND_PRIVATE和PUBLIC_ONLY。

    那么我們打開這個(gè)類,看?下源碼:

    這??的代碼并不復(fù)雜,通過JsonAutoDetect 我們可以看到,Jackson 默認(rèn)不是所有的屬性都可以被序列化和反序列化。默認(rèn)的屬性可視化的規(guī)則如下:

  • 若該屬性修飾符是 public,該屬性可序列化和反序列化。
  • 若屬性的修飾符不是 public,但是它的 getter ?法和 setter ?法是 public,該屬性可序列化和反序列化。因?yàn)?getter ?法?于序列化,? setter ?法?于反序列化。
  • 若屬性只有 public 的 setter ?法,?? public 的 getter ?法,該屬性只能?于反序列化。
  • 所以我們可以通過私有字段的 public get 和 public set ?法控制是否可以序列化。這?可以和我們前?講到的“JPA 實(shí)體??的注解?效?式”做?下對?,也可以通過直接更改 ObjectMapper 設(shè)置可視化策略,如下所示:

    ObjectMapper mapper = new ObjectMapper(); // PropertyAccessor ?持的類型有 ALL,CREATOR,FIELD,GETTER,IS_GETTER,NONE,SETTER // Visibility ?持的類型有 ANY,DEFAULT,NON_PRIVATE,NONE,PROTECTED_AND_PUBLIC,PUBLIC_ONLY mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);

    這樣,就可以直接看到所有字段了,包括私有字段。接著我們說?下反序列化相關(guān)?法。

    8.3.2 反序列化最重要的方法

    我們在做反序列化的時(shí)候要?到的三個(gè)重要?法如下所示。

    public <T> T readValue(String content, Class<T> valueType) public <T> T readValue(String content, TypeReference<T> valueTypeRef) public <T> T readValue(String content, JavaType valueType)

    可以看出,反序列化的時(shí)候要知道 java 的 Type 是很重要的,如下:

    // 單個(gè)對象的寫法: UserJson user = objectMapper.readValue(json, UserJson.class); // 返回List的返回結(jié)果的寫法: List<UserJson> personList2 = objectMapper.readValue(jsonListString, new TypeReference<List<UserJson>>(){}); // 返回List的返回結(jié)果,性能更快的寫法: List<UserJson> users = Arrays.asList(objectMapper.readValue(jsonListString, UserJson[].class));

    我們也可以看一下 AbstractJackson2HttpMessageConverter ??的?法。

    這個(gè)時(shí)候你應(yīng)該很好奇,readValue ??是如何判斷 java 類型的呢?我們看下 ObjectMapper 的源碼??做了如下操作:

    public <T> T readValue(InputStream src, Class<T> valueType)throws IOException, StreamReadException, DatabindException {_assertNotNull("src", src);return (T) _readMapAndClose(_jsonFactory.createParser(src), _typeFactory.constructType(valueType)); }

    到這?,我們看到 typeFactory ??的 constructType 可以取到各種 type,那么點(diǎn)擊進(jìn)去看看。

    可以看到??處理各種 java 類型和泛型的情況,當(dāng)我們??寫反射代碼的時(shí)候可以參考這?段,或者直接調(diào)?。此外,ObjectMapper ??還?個(gè)重要的概念就是 Moduel,我們來看下。

    8.3.3 Module 的加載機(jī)制

    ObejctMapper ??可以擴(kuò)展很多 datatype,?不同的 datatype 封裝到了不通的 modules ??,我們可以 register 注冊進(jìn)去不同的 module,從?處理不同的數(shù)據(jù)類型。

    ?前 Modules 官??站提供了很多內(nèi)容,具體你可以查看這個(gè)?址:https://github.com/FasterXML/jackson#third-party-datatype-modules。這?我們重點(diǎn)說?下常?的加載機(jī)制。

    我們通過在代碼??設(shè)置?個(gè)斷點(diǎn),就可以很清楚地知道常?的 ModuleType 都有哪些,如 Jdk8、jsr310、Hibernate5 等。在MVC ??默認(rèn)的 Module 也是圖上那些,Hibernate5 是我們??引?的,具體解決什么問題和如何?定義的呢?我們接著往下看。

    我們通過在代碼??設(shè)置?個(gè)斷點(diǎn),就可以很清楚地知道常?的 ModuleType 都有哪些,

    如 Jdk8、jsr310、Hibernate5 等。在MVC ??默認(rèn)的 Module 也是圖上那些,Hibernate5 是我們??引?的,具體解決什么問題和如何?定義的呢?我們接著往下看。

    8.4 Jackson 與 JPA 常見的問題

    我們? JPA 的時(shí)候,特別是關(guān)聯(lián)關(guān)系的時(shí)候,最常?的就是死循環(huán)了,你在使?時(shí)?定要注意。

    8.4.1 如何解決死循環(huán)問題

    第?種情況:我們在寫 ToString ?法,特別是 JPA 的實(shí)體的時(shí)候,很容易陷?死循環(huán),因?yàn)閷?shí)體之間的關(guān)聯(lián)關(guān)系配置是雙向的,我們就需要 ToString 的時(shí)候把??排除掉,如下所示:

    第?種情況:在轉(zhuǎn)化JSON的時(shí)候,雙向關(guān)聯(lián)也會(huì)死循環(huán)。按照我們上?講的?法,這是時(shí)候我們要想到通過 @JsonIgnoreProperties(value={"address"}) 或者字段上?配置 @JsonIgnore,如下:

    @JsonIgnore private List<UserAddress> address;

    此外,通過 @JsonBackReference 和 @JsonManagedReference 注解也可以解決死循環(huán)。

    public class UserAddress {@JsonManagedReferenceprivate User user; ...}public class User implements Serializable {@OneToMany(mappedBy = "user",fetch = FetchType.LAZY)@JsonBackReferenceprivate List<UserAddress> address; ...}

    如上述代碼,也可以達(dá)到 @JsonIgnore 的效果,具體你可以??操作?下試試,原理都是?樣的,都是利?排除?法。那么接下來我們看下 HibernateModel5 是怎么使?的。

    8.4.2 JPA 實(shí)體 JSON 序列化的常見報(bào)錯(cuò)及解決方法

    我們在實(shí)際跑之前講過的 user 對象,或者是類似帶有 lazy 對象關(guān)系的時(shí)候,經(jīng)常會(huì)遇到下?的錯(cuò)誤:

    com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: com.example.jpa.example1.entity.User$HibernateProxy$4u6Wef9i["hibernateLazyInitializer"])

    這個(gè)時(shí)候該怎么辦呢?下?介紹?個(gè)解決辦法,第?個(gè)可以引? Hibernate5Module。

    解決?法?:引? Hibernate5Module

    代碼如下:

    ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new Hibernate5Module()); String json = objectMapper.writeValueAsString(user); System.out.println(json);

    這樣?就不會(huì)報(bào)錯(cuò)了。

    Hibernate5Module ??還有很多 Feature 配置,例如 FORCE_LAZY_LOADING,強(qiáng)制 lazy ??加載就不會(huì)有上?的問題了。但是這個(gè)會(huì)有性能問題,我不建議使?。

    還有 USE_TRANSIENT_ANNOTATION,利? JPA 的 @Transient 注解配置,這個(gè)默認(rèn)是開啟的。所以基本上 feature 默認(rèn)配置都是 ok 的,不需要我們動(dòng)?,只要知道這回事就?了。

    解決?法?:關(guān)閉 SerializationFeature.FAIL_ON_EMPTY_BEANS 的 feature

    代碼如下:

    ObjectMapper objectMapper = new ObjectMapper(); // 直接關(guān)閉 SerializationFeature.FAIL_ON_EMPTY_BEANS objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); String json = objectMapper.writeValueAsString(user); System.out.println(json);

    因?yàn)槭?lazy,所以 empty 的 bean 的時(shí)候不報(bào)錯(cuò)也可以。

    解決?法三:對象上?排除“hibernateLazyInitializer”“handler”“fieldHandler”等

    代碼如下:

    @JsonIgnoreProperties(value = {"address", "hibernateLazyInitializer", "handler", "fieldHandler"}) public class User implements Serializable { }

    那有沒有其他 ObjectMapper 的推薦配置了呢?

    8.4.3 推薦的配置項(xiàng)

    ObjectMapper objectMapper = new ObjectMapper(); // empty beans不需要報(bào)錯(cuò),沒有就是沒有了 objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); // 遇到不可識(shí)別字段的時(shí)候不要報(bào)錯(cuò),因?yàn)榍岸藗鬟M(jìn)來的字段不可信,可以不要影響正常業(yè)務(wù)邏輯 objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // 遇到不可以識(shí)別的枚舉的時(shí)候,為了保證服務(wù)的強(qiáng)壯性,建議也不要關(guān)?未知的,甚?給個(gè)默認(rèn)的,特別是微服務(wù)?家的枚舉值隨時(shí)在變,但是?的服務(wù)是不需要跟著?起變的 objectMapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true); objectMapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE, true);

    時(shí)間類型的最佳實(shí)踐,如何返回 ISO 格式的標(biāo)準(zhǔn)時(shí)間

    有的時(shí)候我們會(huì)發(fā)現(xiàn),默認(rèn)的 ObjectMapper ??的 module 提供的時(shí)間轉(zhuǎn)化格式可能不能滿?我們的要求,可能要進(jìn)?擴(kuò)展,這里提供?個(gè)?定義 module 返回 ISO 標(biāo)準(zhǔn)時(shí)間格式的?個(gè)案例,如下:

    @Test @Rollback(false) public void testUserJson2() throws JsonProcessingException {UserJson userJson = userJsonRepository.findById(1L).get();userJson.setOther(Maps.newHashMap("address", "shanghai"));//?定義 myInstant 解析序列化和反序列化 DateTimeFormatter.ISO_ZONED_DATE_TIME 這種格式SimpleModule myInstant = new SimpleModule("instant", Version.unknownVersion()).addSerializer(java.time.Instant.class, new JsonSerializer<Instant>() {@Overridepublic void serialize(java.time.Instant instant,JsonGenerator jsonGenerator,SerializerProvider serializerProvider)throws IOException {if (instant == null) {jsonGenerator.writeNull();} else {jsonGenerator.writeObject(instant.toString());}}}).addDeserializer(Instant.class, new JsonDeserializer<Instant>() {@Overridepublic Instant deserialize(JsonParser jsonParser,DeserializationContext deserializationContext) throws IOException {Instant result = null;String text = jsonParser.getText();if (!StringUtils.hasText(text)) {result = ZonedDateTime.parse(text,DateTimeFormatter.ISO_ZONED_DATE_TIME).toInstant();}return result;}});ObjectMapper objectMapper = new ObjectMapper();// 注冊?定義的moduleobjectMapper.registerModule(myInstant);String json = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(userJson);System.out.println(json); }

    我們利?上?的 UserJson 案例,在測試?例???定義了 myInstant 來進(jìn)?序列化和反序列化Instant這種類型,然后我們通過 objectMapper.registerModule(myInstant); 注冊進(jìn)去。那么我們看?下運(yùn)?結(jié)果:

    {"createDate" : "2022-03-20T01:46:49.466Z","email" : "123456@126.com","id" : 1,"updateDate" : "2022-03-20 09:46","my_name" : "jackxx","address" : "shanghai" }

    這時(shí)你會(huì)發(fā)現(xiàn) createDate 的格式發(fā)?了變化,這樣?的話,任何?看到我們這樣的 JSON 結(jié)構(gòu)就不必問我們到底是哪個(gè)時(shí)區(qū)的問題了。

    8.5 本章小結(jié)

    到這?,關(guān)于 Spring Data JPA 的基礎(chǔ)知識(shí)也告?段落,這?課時(shí)詳細(xì)講解了 Jackson 的原理,分析了?下 JPA ??經(jīng)常會(huì)遇到的問題,并推薦了?些常?配置。有個(gè)需要注意的點(diǎn)就是雙向關(guān)聯(lián)關(guān)系,如果你暫時(shí)不得要領(lǐng)的話,我建議不要為了???,我們就按照 DB 的真實(shí)映射寫法就可以,類似 MyBatis ?樣,只不過不需要我們?nèi)リP(guān)?和配置映射關(guān)系。

    這?我還想說?個(gè)解題思路,就是當(dāng)我們遇到問題的時(shí)候,要學(xué)著挖?挖問題的根源,這樣解決問題才能夠游刃有余。

    總結(jié)

    以上是生活随笔為你收集整理的Spring data JPA 之 Jackson 在实体里面的注解详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。