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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

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

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

8 Spring data JPA 之 Jackson 在實體里面的注解詳解

經過前?課時的講解,相信你已經對實體??的 JPA 注解有了?定的了解,但是實際?作中你會發現實體??不僅有 JPA 的注解,也會?到很多 JSON 相關的注解。

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

8.1 Jackson 的基本語法

當我們? spring boot starter 的時候就會默認加載 fasterxml 相關的 jar 包模塊,包括核?模塊以及 jackson 提供的?些擴展 jar 包,下?詳細介紹。

8.1.1 三個核心模塊

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

  • jackson-annotations:注解包,提供標準注解功能,這是我們必須要掌握的基礎語法。

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

  • 8.1.2 Jackson 提供的擴展 jar 包

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

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

    8.1.3 Jackson 中常用的一些注解

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

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

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

    8.1.4 實例

    ?先,新建?個 UserJson 實體對象,將它轉成 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("其他業務邏輯");this.email = email;}@Transient@JsonAnySetterprivate Map<String, Object> other = new HashMap<>();@JsonAnyGetterpublic Map<String, Object> getOther() {return other;} }

    然后,我們寫?個測試?例,看?下運?結果。

    @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));} }

    最后,運??下可以看到如下結果。

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

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

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

    8.2 Jackson 和 Spring 的關系

    8.2.1 應用場景一:Spring MVC 的 View 層

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

    ???的 MappingJackson2HttpMessageConverter 正是采? fasterxml.jackson 進?轉化的,看下?的圖?。

    8.2.2 應用場景二:Open-Feign

    我們在微服務之間相互調?的時候,都會?到 HttpMessageConverter ??的JacksonHttpMessageConverter 進?轉化。特別是在? open-feign ??的 Encode 和 Decode 的時候,我們就可以看到如下應?場景:

    8.2.3 應用場景三:Redis 里面

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

    8.2.4 應用場景四:JMS 消息序列化

    當我們項?之間解耦?到消息隊列的時候,可能會基于 JMS 消息協議發送消息,其也是基于 JSON 的序列化機制來繼續 converter 的,它在? JmsTemplate 的時候也會遇到同樣情況。

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

    8.3 Jackson 的原理分析

    8.3.1 Jackson 的可見性原理分析

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

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

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

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

    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);

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

    8.3.2 反序列化最重要的方法

    我們在做反序列化的時候要?到的三個重要?法如下所示。

    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)

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

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

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

    這個時候你應該很好奇,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,那么點擊進去看看。

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

    8.3.3 Module 的加載機制

    ObejctMapper ??可以擴展很多 datatype,?不同的 datatype 封裝到了不通的 modules ??,我們可以 register 注冊進去不同的 module,從?處理不同的數據類型。

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

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

    我們通過在代碼??設置?個斷點,就可以很清楚地知道常?的 ModuleType 都有哪些,

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

    8.4 Jackson 與 JPA 常見的問題

    我們? JPA 的時候,特別是關聯關系的時候,最常?的就是死循環了,你在使?時?定要注意。

    8.4.1 如何解決死循環問題

    第?種情況:我們在寫 ToString ?法,特別是 JPA 的實體的時候,很容易陷?死循環,因為實體之間的關聯關系配置是雙向的,我們就需要 ToString 的時候把??排除掉,如下所示:

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

    @JsonIgnore private List<UserAddress> address;

    此外,通過 @JsonBackReference 和 @JsonManagedReference 注解也可以解決死循環。

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

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

    8.4.2 JPA 實體 JSON 序列化的常見報錯及解決方法

    我們在實際跑之前講過的 user 對象,或者是類似帶有 lazy 對象關系的時候,經常會遇到下?的錯誤:

    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"])

    這個時候該怎么辦呢?下?介紹?個解決辦法,第?個可以引? Hibernate5Module。

    解決?法?:引? Hibernate5Module

    代碼如下:

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

    這樣?就不會報錯了。

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

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

    解決?法?:關閉 SerializationFeature.FAIL_ON_EMPTY_BEANS 的 feature

    代碼如下:

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

    因為是 lazy,所以 empty 的 bean 的時候不報錯也可以。

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

    代碼如下:

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

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

    8.4.3 推薦的配置項

    ObjectMapper objectMapper = new ObjectMapper(); // empty beans不需要報錯,沒有就是沒有了 objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); // 遇到不可識別字段的時候不要報錯,因為前端傳進來的字段不可信,可以不要影響正常業務邏輯 objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // 遇到不可以識別的枚舉的時候,為了保證服務的強壯性,建議也不要關?未知的,甚?給個默認的,特別是微服務?家的枚舉值隨時在變,但是?的服務是不需要跟著?起變的 objectMapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true); objectMapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE, true);

    時間類型的最佳實踐,如何返回 ISO 格式的標準時間

    有的時候我們會發現,默認的 ObjectMapper ??的 module 提供的時間轉化格式可能不能滿?我們的要求,可能要進?擴展,這里提供?個?定義 module 返回 ISO 標準時間格式的?個案例,如下:

    @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 來進?序列化和反序列化Instant這種類型,然后我們通過 objectMapper.registerModule(myInstant); 注冊進去。那么我們看?下運?結果:

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

    這時你會發現 createDate 的格式發?了變化,這樣?的話,任何?看到我們這樣的 JSON 結構就不必問我們到底是哪個時區的問題了。

    8.5 本章小結

    到這?,關于 Spring Data JPA 的基礎知識也告?段落,這?課時詳細講解了 Jackson 的原理,分析了?下 JPA ??經常會遇到的問題,并推薦了?些常?配置。有個需要注意的點就是雙向關聯關系,如果你暫時不得要領的話,我建議不要為了???,我們就按照 DB 的真實映射寫法就可以,類似 MyBatis ?樣,只不過不需要我們去關?和配置映射關系。

    這?我還想說?個解題思路,就是當我們遇到問題的時候,要學著挖?挖問題的根源,這樣解決問題才能夠游刃有余。

    總結

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

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。