动态

详情 返回 返回

jpa之hibernate和jackson踩坑記錄 - 动态 详情

在做的項目採用的是spring jpa,底層默認使用的是orm是hibernate,通過hibernate查詢出來的實體對象實際上都是代理對象,在序列化的時候,我們可能會遇到懶加載導致jackson無法正確解析對象的問題,這個可以通過導入maven包

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-hibernate5</artifactId>
</dependency>

然後加載進objectMapper上下文

@Bean
public Jackson2ObjectMapperBuilderCustomizer builderCustomizer() {
    return builder -> {
        //jackson序列化 和 hibernate懶加載
        builder.modulesToInstall(hibernate5Module());
    };
}

private static Hibernate5Module hibernate5Module() {
    Hibernate5Module hibernate5Module = new Hibernate5Module();
    hibernate5Module.configure(REPLACE_PERSISTENT_COLLECTIONS, true);
    //hibernate5Module.configure(FORCE_LAZY_LOADING, true);
    return hibernate5Module;
}

這樣在序列化的時候就會判斷之前是否存在數據了,如果是懶加載則會序列化為null。


我在反序列化的時候會報failed to lazily initialize a collection, could not initialize proxy...,後來看了一下序列化後的json,發現有些延遲加載的Collection類型的會解析成org.hibernate.collection.internal.PersistentBag(hibernate的代理對象),而不是咱們一般json的集合形式。導致反序列化的時候,會生成PersistentBag對象,這時候jackson操作PersistentBag,會導致hibernate檢測到數據變動,從而觸發它的一些操作而報上面的錯。

然後我就排查,為啥序列化的時候會轉成PersistentBag對象呢?可以看看Hibernate5Module裏的代碼,該模塊會加載兩個

context.addSerializers(new HibernateSerializers(_mapping, _moduleFeatures));
context.addBeanSerializerModifier(new HibernateSerializerModifier(_moduleFeatures, _sessionFactory));

//序列化處理器,咱們之前的處理懶加載原理也是通過這兩個處理的,然後我在
HibernateSerializerModifier裏看到了PersistentCollectionSerializer,對於集合類型的解析處理,其中主要有個判斷方法
protected boolean usesLazyLoading(BeanProperty property) {
    if (property != null) {
        // As per [Issue#36]
        ElementCollection ec = property.getAnnotation(ElementCollection.class);
        if (ec != null) {
            return (ec.fetch() == FetchType.LAZY);
        }
        OneToMany ann1 = property.getAnnotation(OneToMany.class);
        if (ann1 != null) {
            return (ann1.fetch() == FetchType.LAZY);
        }
        OneToOne ann2 = property.getAnnotation(OneToOne.class);
        if (ann2 != null) {
            return (ann2.fetch() == FetchType.LAZY);
        }
        ManyToOne ann3 = property.getAnnotation(ManyToOne.class);
        if (ann3 != null) {
            return (ann3.fetch() == FetchType.LAZY);
        }
        ManyToMany ann4 = property.getAnnotation(ManyToMany.class);
        if (ann4 != null) {
            return (ann4.fetch() == FetchType.LAZY);
        }
        // As per [Issue#53]
        return !Feature.REQUIRE_EXPLICIT_LAZY_LOADING_MARKER.enabledIn(_features);
    }
    return false;
}

發現我關聯字段上使用的是@ManyToMany(fetch = FetchType.EAGER),因為我之前為了處理懶加載就加上的,現在可以去掉了,問題也得到了解決。

user avatar king_wenzhinan 头像 u_17513518 头像 u_11365552 头像 u_15702012 头像 lu_lu 头像 chaochenyinshi 头像 huangxunhui 头像 devlive 头像 lanlan_guo 头像 yangrd 头像 itxiaoma 头像 daimajiangxin 头像
点赞 36 用户, 点赞了这篇动态!
点赞

Add a new 评论

Some HTML is okay.