【问题标题】:How to avoid lazy fetch in JSON serialization using Spring Data JPA + Spring Web MVC?如何使用 Spring Data JPA + Spring Web MVC 避免 JSON 序列化中的延迟获取?
【发布时间】:2015-04-29 01:46:26
【问题描述】:

我有一个在 Spring Web MVC 中使用 Spring Data JPA 和 REST 控制器的解决方案。持久性提供程序是 Hibernate。

持久层是使用 Spring Repositories 构建的,在 de REST Controller 和 repository 之间存在一个 Service Layer:

Entity <--> Repository <--> Service <--> Controller

在实体级别,我有 FetchType = LAZY 的 @OneToMany 字段。

当 REST 控制器进行序列化时,会进行提取,但在某些情况下这是不可取的。

我已经尝试过使用@JSONInclude Jackson 注释并且序列化仍然发生。

有人可以帮我提供一个经过验证的解决方案吗?

【问题讨论】:

标签: spring hibernate spring-mvc jpa spring-data


【解决方案1】:

我们可以通过将 Hibernate4Module(支持惰性对象)添加到 Spring 已经提供的默认 MappingJackson2HttpMessageConverter 并将其添加到 HttpMessageConverters 来做到这一点。

所以我们需要从 WebMvcConfigurerAdapter 扩展我们的 spring 配置类并覆盖方法 configureMessageConverters 并添加 MappingJackson2HttpMessageConverter 并在其中注册 Hibernate4Module。

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    //Here we add our custom-configured HttpMessageConverter
    converters.add(jacksonMessageConverter());
    super.configureMessageConverters(converters);
}

// Here we register the Hibernate4Module into an ObjectMapper, 
// then use this custom ObjectMapper
// to the MessageConverter
public MappingJackson2HttpMessageConverter jacksonMessageConverter(){
    MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();

    ObjectMapper mapper = new ObjectMapper();
    //Registering Hibernate4Module to support lazy objects
    mapper.registerModule(new Hibernate4Module());

    messageConverter.setObjectMapper(mapper);
    return messageConverter;

}

【讨论】:

  • 这将永久禁用字段的获取和序列化。我想要的是避免在序列化时获取。如果使用 JPQL 完成提取,则必须完成序列化。
【解决方案2】:

如果我理解正确,您只想在延迟加载的集合被提取时进行序列化,但您不希望序列化触发提取。

如果是这种情况,您应该使用jackson-datatype-hibernate,并按照他们的文档已经解释的方式添加

public class HibernateAwareObjectMapper extends ObjectMapper {

    public HibernateAwareObjectMapper() {
        registerModule(new Hibernate4Module());
    }
}

比注册

 <mvc:annotation-driven>
        <mvc:message-converters>
            <!-- Use the HibernateAware mapper instead of the default -->
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper">
                    <bean class="path.to.your.HibernateAwareObjectMapper" />
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

模块有一个 Feature.FORCE_LAZY_LOADING 设置,它告诉对象是否应该被强制加载然后序列化,默认设置为 false,我相信是你需要的行为

【讨论】:

  • 这完全解决了我需要的问题。非常感谢!
【解决方案3】:

简单的架构解决方案是不使用模型实体作为数据传输对象。将 Simple POJO 作为数据传输对象。在转换逻辑中,您可以轻松地为 LazyInitializationException 放置 try 和 catch 块。因此您的 POJO 始终是可序列化的,您可以在控制器上使用它。

【讨论】:

    【解决方案4】:

    如果您总是想跳过此特定字段,请使用 @JsonIgnore 注释。如果要动态确定要跳过的字段,请使用 @JsonView。请注意,@JsonView 是 Jackson 特定的注释,但由于您已经在使用 Jackson,所以应该没问题。

    【讨论】:

    • @JsonView 看起来很不错。我将对其进行测试,看看这是否可以避免懒惰的获取……我会告诉你这是怎么回事……
    • 我尝试使用 JsonView 失败。考虑我的问题,使用 JsonView 不适合大型和复杂的模型场景。无论如何,谢谢你的解决方案。
    【解决方案5】:

    您可以通过jackson-datatype-hibernate 的两个步骤解决此问题:

    kotlin 示例

    1. 添加build.gradle.kts:
    implementation("com.fasterxml.jackson.datatype:jackson-datatype-hibernate5:$jacksonHibernate")
    
    1. 创建@Bean
       @Bean
       fun hibernate5Module(): Module = Hibernate5Module()
    

    • 注意Modulecom.fasterxml.jackson.databind.Module,而不是java.util.Module

    • 另外一个好的做法是将@JsonBackReference & @JsonManagedReference 添加到@OneToMany & @ManyToOne 关系中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-29
      • 1970-01-01
      • 2011-08-07
      • 2015-08-08
      • 2018-11-07
      • 1970-01-01
      相关资源
      最近更新 更多