Éviter la sérialisation de Jackson sur les objets paresseux non récupérés

j'ai un simple contrôleur qui renvoie un objet Utilisateur, cet utilisateur a des coordonnées d'attribut qui ont la propriété hibernate FetchType.PARESSEUX.

quand j'essaie d'obtenir cet utilisateur, je dois toujours charger toutes les coordonnées pour obtenir l'objet utilisateur, sinon quand Jackson essayer de sérialiser L'utilisateur lance l'exception:

com.fasterxml.Jackson.databind.JsonMappingException: impossible d'initialiser proxy-pas de Session

C'est dû au fait que Jackson essaie de récupérer cet objet non tracé. Voici les objets:

public class User{

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
    @JsonManagedReference("user-coordinate")
    private List<Coordinate> coordinates;
}

public class Coordinate {

    @ManyToOne
    @JoinColumn(name = "user_id", nullable = false)
    @JsonBackReference("user-coordinate")
    private User user;
}

et le contrôleur:

@RequestMapping(value = "/user/{username}", method=RequestMethod.GET)
public @ResponseBody User getUser(@PathVariable String username) {

    User user = userService.getUser(username);

    return user;

}

il y a un moyen de dire à Jackson de ne pas sérialiser les objets non gravés? J'ai cherché d'autres réponses postées il y a 3 ans implémentant le module jackson-hibernate. Mais probablement il pourrait être réalisé avec un nouveau jackson fonctionnalité.

mes versions sont:

  • printemps 3.2.5
  • hibernation 4.1.7
  • Jackson 2.2

Merci d'avance.

55
demandé sur Cassio Mazzochi Molin 2014-02-11 21:07:14

10 réponses

j'ai enfin trouvé la solution! merci à indybee de m'avoir donné un indice.

le tutoriel printemps 3.1, Hibernate 4 et Jackson-Module-Hibernate ont une bonne solution pour le printemps 3.1 et les versions précédentes. Mais depuis la version 3.1.2 Spring ont son propre MappingJackson2HttpMessageConverter avec presque la même fonctionnalité que celle dans le tutoriel, donc nous n'avons pas besoin de créer ce HTTPMessageConverter personnalisé.

avec javaconfig nous n'avons pas besoin de créer un HibernateAwareObjectMapper aussi, nous avons juste besoin d'ajouter le Hibernate4Module à la valeur par défaut MappingJackson2HttpMessageConverter que le printemps ont déjà et l'Ajouter aux Httpmessageconverter de l'application, nous devons donc:

  1. étendre notre classe de configuration de ressort de WebMvcConfigurerAdapter et remplacer la méthode configureMessageConverters .

  2. sur cette méthode, ajouter le MappingJackson2HttpMessageConverter avec le Hibernate4Module enregistré dans une méthode antérieure.

notre classe de configuration devrait ressembler à ceci:

@Configuration
@EnableWebMvc
public class MyConfigClass extends WebMvcConfigurerAdapter{

    //More configuration....

    /* Here we register the Hibernate4Module into an ObjectMapper, then set this custom-configured ObjectMapper
     * to the MessageConverter and return it to be added to the HttpMessageConverters of our application*/
    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;

    }

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

    //More configuration....
}

si vous avez une configuration xml, vous ne besoin de créer votre propre MappingJackson2HttpMessageConverter soit, mais vous avez besoin de créer le mapper personnalisé qui apparaît dans le tutoriel (HibernateAwareObjectMapper), donc votre config xml devrait ressembler à ceci:

<mvc:message-converters>
    <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
        <property name="objectMapper">
            <bean class="com.pastelstudios.json.HibernateAwareObjectMapper" />
        </property>
    </bean>
</mvc:message-converters>

espérons que cette réponse soit compréhensible et aide quelqu'un à trouver la solution à ce problème, n'hésitez pas à poser des questions!

81
répondu r1ckr 2014-02-13 16:48:56

à partir du printemps 4.2 et en utilisant la botte de printemps et javaconfig, enregistrer le Hibernate4Module est maintenant aussi simple que de l'ajouter à votre configuration:

@Bean
public Module datatypeHibernateModule() {
  return new Hibernate4Module();
}

réf: https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring

18
répondu chrismarx 2016-06-15 16:05:07

c'est similaire à la solution acceptée par @rick.

si vous ne voulez pas toucher à la configuration existante des convertisseurs de messages, Vous pouvez simplement déclarer un Jackson2ObjectMapperBuilder bean like:

@Bean
public Jackson2ObjectMapperBuilder configureObjectMapper() {
    return new Jackson2ObjectMapperBuilder()
        .modulesToInstall(Hibernate4Module.class);
}

n'oubliez pas d'ajouter la dépendance suivante à votre fichier Gradle (ou Maven):

compile 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate4:2.4.4'

utile si vous avez un application et vous voulez garder la possibilité de modifier Jackson caractéristiques du fichier application.properties .

14
répondu Luis Belloch 2015-01-27 21:01:32

dans le cas de données de printemps reste alors, alors que la solution Postée par @r1ckr fonctionne, tout ce qui est nécessaire est d'ajouter une des dépendances suivantes en fonction de votre version D'hibernation:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-hibernate4</artifactId>
    <version>${jackson.version}</version>
</dependency>

ou

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-hibernate5</artifactId>
    <version>${jackson.version}</version>
</dependency>

dans le repos de données de printemps il y a une classe:

org.springframework.data.rest.webmvc.json.Jackson2DatatypeHelper

qui détectera automatiquement et enregistrera le Module au démarrage de l'application.

Il y a cependant un problème:

Problème De Sérialisation Paresseux @ManyToOne

6
répondu Alan Hay 2017-05-23 10:31:29

si vous utilisez XML config et utilisez <annotation-driven /> , j'ai trouvé que vous devez faire un nid <message-converters> à l'intérieur de <annotation-driven> comme recommandé sur le compte Jackson Github .

Comme suit:

<annotation-driven>
  <message-converters>
    <beans:bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
      <beans:property name="objectMapper">
        <beans:bean class="com.pastelstudios.json.HibernateAwareObjectMapper" />
      </beans:property>
    </beans:bean> 
  </message-converters>
</annotation-driven>

`

5
répondu blackwood 2014-08-24 17:39:55

pour ceux qui sont venus ici à la recherche de la solution pour le service RESTful basé sur Apache CXF, la configuration qui le corrige est ci-dessous:

<jaxrs:providers>
    <bean class="com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider">
        <property name="mapper" ref="objectMapper"/>
    </bean>
</jaxrs:providers>

<bean id="objectMapper" class="path.to.your.HibernateAwareObjectMapper"/>

où HibernateAwareObjectMapper est défini comme:

public class HibernateAwareObjectMapper extends ObjectMapper {
    public HibernateAwareObjectMapper() {
        registerModule(new Hibernate5Module());
    }
}

la dépendance suivante est requise à partir de juin 2016 (à condition que vous utilisiez Hibernate5):

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-hibernate5</artifactId>
    <version>2.7.4</version>
</dependency>  
3
répondu user2968675 2016-06-09 04:58:17

vous pouvez utiliser l'aide suivante du projet Spring Data Rest:

Jackson2DatatypeHelper.configureObjectMapper(objectMapper);
1
répondu Przemek Nowak 2014-09-02 12:15:27

j'ai essayé la réponse utile de @rick's , mais j'ai rencontré le problème que les" modules bien connus "tels que jackson-datatype-jsr310 n'étaient pas automatiquement enregistrés alors qu'ils étaient sur le chemin de classe. (Ce post de blog explique l'auto-inscription.)

en prolongeant la réponse de @rick's, voici une variation utilisant le Jackson2ObjectMapperBuilder "151950920 de Spring pour créer le ObjectMapper . Cela enregistre automatiquement les" modules bien connus "et définit certaines fonctionnalités en plus de l'installation du Hibernate4Module .

@Configuration
@EnableWebMvc
public class MyWebConfig extends WebMvcConfigurerAdapter {

    // get a configured Hibernate4Module
    // here as an example with a disabled USE_TRANSIENT_ANNOTATION feature
    private Hibernate4Module hibernate4Module() {
        return new Hibernate4Module().disable(Hibernate4Module.Feature.USE_TRANSIENT_ANNOTATION);
    }

    // create the ObjectMapper with Spring's Jackson2ObjectMapperBuilder
    // and passing the hibernate4Module to modulesToInstall()
    private MappingJackson2HttpMessageConverter jacksonMessageConverter(){
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
            .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
            .modulesToInstall(hibernate4Module());
        return new MappingJackson2HttpMessageConverter(builder.build());
  }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(jacksonMessageConverter());
        super.configureMessageConverters(converters);
    }
}
0
répondu Markus Pscheidt 2017-05-23 12:34:39

bien que cette question soit légèrement différente de celle-ci : Exception Jackson étrange étant lancé lors de la sérialisation de L'objet Hibernate , le problème sous-jacent peut être corrigé de la même manière avec ce code:

@Provider
public class MyJacksonJsonProvider extends JacksonJsonProvider {
    public MyJacksonJsonProvider() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        setMapper(mapper);
    }
}
0
répondu nevster 2017-05-23 12:34:39

j'ai essayé et j'ai travaillé:

// configuration personnalisée pour le chargement paresseux

public static class HibernateLazyInitializerSerializer extends JsonSerializer<JavassistLazyInitializer> {

    @Override
    public void serialize(JavassistLazyInitializer initializer, JsonGenerator jsonGenerator,
            SerializerProvider serializerProvider)
            throws IOException, JsonProcessingException {
        jsonGenerator.writeNull();
    }
}

et configurer mapper:

    mapper = new JacksonMapper();
    SimpleModule simpleModule = new SimpleModule(
            "SimpleModule", new Version(1,0,0,null)
    );
    simpleModule.addSerializer(
            JavassistLazyInitializer.class,
            new HibernateLazyInitializerSerializer()
    );
    mapper.registerModule(simpleModule);
0
répondu ahll 2016-08-26 10:54:32