Ich habe einen einfachen Controller, der ein Benutzerobjekt zurückgibt. Dieser Benutzer hat Attributkoordinaten mit der Eigenschaft FetchType.LAZY im Ruhezustand.
Wenn ich versuche, diesen Benutzer zu erhalten, muss ich immer alle Koordinaten laden, um das Benutzerobjekt zu erhalten. Andernfalls löst Jackson beim Versuch, den Benutzer zu serialisieren, die Ausnahme aus:
com.fasterxml.jackson.databind.JsonMappingException: Proxy konnte nicht initialisiert werden - keine Sitzung
Dies liegt daran, dass Jackson versucht, dieses nicht abgerufene Objekt abzurufen. Hier sind die Objekte:
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;
}
Und der Controller:
@RequestMapping(value = "/user/{username}", method=RequestMethod.GET)
public @ResponseBody User getUser(@PathVariable String username) {
User user = userService.getUser(username);
return user;
}
Gibt es eine Möglichkeit, Jackson anzuweisen, die nicht abgerufenen Objekte nicht zu serialisieren? Ich habe nach anderen Antworten gesucht, die vor 3 Jahren veröffentlicht wurden, als das Jackson-Hibernate-Modul implementiert wurde. Aber wahrscheinlich könnte es mit einer neuen Jackson-Funktion erreicht werden.
Meine Versionen sind:
- Feder 3.2.5
- Ruhezustand 4.1.7
- Jackson 2.2
Danke im Voraus.
Antworten:
Ich habe endlich die Lösung gefunden! danke an indybee für den hinweis.
Das Tutorial Spring 3.1, Hibernate 4 und Jackson-Module-Hibernate bieten eine gute Lösung für Spring 3.1 und frühere Versionen. Da Version 3.1.2 Spring jedoch über einen eigenen MappingJackson2HttpMessageConverter verfügt, der fast die gleiche Funktionalität wie im Lernprogramm aufweist, müssen wir diesen benutzerdefinierten HTTPMessageConverter nicht erstellen.
Mit javaconfig müssen wir auch keinen HibernateAwareObjectMapper erstellen . Wir müssen nur das Hibernate4Module zu dem Standard- MappingJackson2HttpMessageConverter hinzufügen , den Spring bereits hat, und es zu den HttpMessageConverters der Anwendung hinzufügen, also müssen wir:
Erweitern Sie unsere Spring-Konfigurationsklasse von WebMvcConfigurerAdapter und überschreiben Sie die Methode configureMessageConverters .
Fügen Sie bei dieser Methode den MappingJackson2HttpMessageConverter mit dem in einer vorherigen Methode registrierten Hibernate4Module hinzu.
Unsere Konfigurationsklasse sollte folgendermaßen aussehen:
@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.... }
Wenn Sie eine XML-Konfiguration haben, müssen Sie auch keinen eigenen MappingJackson2HttpMessageConverter erstellen, aber Sie müssen den personalisierten Mapper erstellen, der im Lernprogramm angezeigt wird (HibernateAwareObjectMapper), sodass Ihre XML-Konfiguration folgendermaßen aussehen sollte:
<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>
Hoffe, diese Antwort ist verständlich und hilft jemandem, die Lösung für dieses Problem zu finden. Fragen können Sie gerne stellen!
quelle
Ab Spring 4.2 und unter Verwendung von Spring Boot und javaconfig ist das Registrieren des Hibernate4Module jetzt so einfach wie das Hinzufügen zu Ihrer Konfiguration:
@Bean public Module datatypeHibernateModule() { return new Hibernate4Module(); }
ref: https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring
quelle
Hibernate5Module
. Es braucht zusätzlich eine Abhängigkeit von<groupId>com.fasterxml.jackson.datatype</groupId><artifactId>jackson-datatype-hibernate5</artifactId>
Dies ähnelt der von @rick akzeptierten Lösung.
Wenn Sie die vorhandene Konfiguration von Nachrichtenkonvertern nicht berühren möchten, können Sie einfach eine
Jackson2ObjectMapperBuilder
Bean wie folgt deklarieren :@Bean public Jackson2ObjectMapperBuilder configureObjectMapper() { return new Jackson2ObjectMapperBuilder() .modulesToInstall(Hibernate4Module.class); }
Vergessen Sie nicht, Ihrer Gradle-Datei (oder Maven) die folgende Abhängigkeit hinzuzufügen:
compile 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate4:2.4.4'
Nützlich, wenn Sie eine haben FederstiefelAnwendung und Sie möchten die Möglichkeit behalten, Jackson-Funktionen aus der
application.properties
Datei zu ändern .quelle
application.properties
Datei erreichen (Jackson konfigurieren, um ein verzögert geladenes Objekt zu vermeiden) ?Hibernate4Module
ist VermächtnisIm Fall von Spring Data Rest müssen Sie, während die von @ r1ckr veröffentlichte Lösung funktioniert, abhängig von Ihrer Hibernate-Version nur eine der folgenden Abhängigkeiten hinzufügen:
oder
Innerhalb von Spring Data Rest gibt es eine Klasse:
org.springframework.data.rest.webmvc.json.Jackson2DatatypeHelper
Dadurch wird das Modul beim Start der Anwendung automatisch erkannt und registriert.
Es gibt jedoch ein Problem:
Problem beim Serialisieren von Lazy @ManyToOne
quelle
@Bean hibernate5Module()
Wenn Sie XML - Konfigurations und Nutzung verwenden
<annotation-driven />
, fand ich , dass Sie nisten haben<message-converters>
innen<annotation-driven>
wie auf dem empfohlenen Jackson Github - Konto .Wie so:
<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>
`
quelle
Für diejenigen, die hierher gekommen sind, um die Lösung für den Apache CXF-basierten RESTful-Service zu finden, ist die Konfiguration, die dies behebt, unten aufgeführt:
<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"/>
Wobei HibernateAwareObjectMapper definiert ist als:
public class HibernateAwareObjectMapper extends ObjectMapper { public HibernateAwareObjectMapper() { registerModule(new Hibernate5Module()); } }
Die folgende Abhängigkeit ist ab Juni 2016 erforderlich (vorausgesetzt, Sie verwenden Hibernate5):
<dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-hibernate5</artifactId> <version>2.7.4</version> </dependency>
quelle
Die folgende Lösung ist für Spring 4.3 (ohne Boot) und Hibernate 5.1 vorgesehen, bei dem alle Fetchtypen
fetch=FetchType.LAZY
ausfetch=FetchType.EAGER
Leistungsgründen von Fällen auf umgestellt wurden. Sofort sahen wir diecom.fasterxml.jackson.databind.JsonMappingException: could not initialize proxy
Ausnahme aufgrund des Problems des verzögerten Ladens.Zunächst fügen wir die folgende Maven-Abhängigkeit hinzu:
Anschließend wird unserer Java MVC-Konfigurationsdatei Folgendes hinzugefügt:
@Configuration @EnableWebMvc public class MvcConfig extends WebMvcConfigurerAdapter { @Override public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { Hibernate5Module h5module = new Hibernate5Module(); h5module.disable(Hibernate5Module.Feature.USE_TRANSIENT_ANNOTATION); h5module.enable(Hibernate5Module.Feature.FORCE_LAZY_LOADING); for (HttpMessageConverter<?> mc : converters){ if (mc instanceof MappingJackson2HttpMessageConverter || mc instanceof MappingJackson2XmlHttpMessageConverter) { ((AbstractJackson2HttpMessageConverter) mc).getObjectMapper().registerModule(h5module); } } return; }
Anmerkungen:
Sie müssen das Hibernate5Module erstellen und konfigurieren, um ein ähnliches Verhalten wie bei Jackson ohne dieses Modul zu erzielen. Die Standardeinstellung macht inkompatible Annahmen.
Unsere
WebMvcConfigurerAdapter
enthält viele andere Konfigurationen und wir wollten eine andere Konfigurationsklasse vermeiden, weshalb wir dieWebMvcConfigurationSupport#addDefaultHttpMessageConverters
Funktion, auf die in anderen Posts verwiesen wurde, nicht verwendet haben .WebMvcConfigurerAdapter#configureMessageConverters
Deaktiviert die gesamte interne Konfiguration der Nachrichtenkonverter von Spring. Wir haben es vorgezogen, mögliche Probleme zu vermeiden.Verwenden des
extendMessageConverters
aktivierten Zugriffs auf alle automatisch konfigurierten Jackson-Klassen, ohne die Konfiguration aller anderen Nachrichtenkonverter zu verlieren.Mit konnten
getObjectMapper#registerModule
wir dieHibernate5Module
zu den vorhandenen Konvertern hinzufügen .Dieser Zusatz löste das Problem mit dem Ruhezustand und dem verzögerten Laden, verursachte jedoch ein Restproblem mit dem generierten JSON-Format . Wie in diesem Github-Problem berichtet , ignoriert das Lazy-Load-Load-Modul von Hibernate-Jackson derzeit die Annotation @JsonUnwrapped, was zu potenziellen Datenfehlern führt. Dies geschieht unabhängig von der Einstellung der Kraftbelastungsfunktion. Das Problem besteht seit 2016.
Hinweis
Es scheint, dass der integrierte ObjectMapper durch Hinzufügen von Folgendes zu Klassen, die verzögert geladen werden, ohne Hinzufügen des Moduls hibernate5 funktioniert:
@JsonIgnoreProperties( {"handler","hibernateLazyInitializer"} ) public class Anyclass {
quelle
kotlin @Bean fun hibernate5Module(): Module = Hibernate5Module()
Sie können den folgenden Helfer aus dem Spring Data Rest-Projekt verwenden:
quelle
Ich habe eine sehr einfache Lösung für dieses Problem gefunden.
@JsonInclude(JsonInclude.Include.NON_EMPTY) public Set<Pendency> getPendencies() { return Hibernate.isInitialized(this.pendencies) ? Collections.unmodifiableSet(this.pendencies) : new HashSet<>(); }
In meinem Fall habe ich den Fehler angegeben, weil ich ihn bei jeder Rückgabe von Abhängigkeiten als bewährte Methode in eine Liste konvertiert habe, die nicht geändert werden kann, die jedoch abhängig von der Methode, mit der ich die Instanz abgerufen habe, verzögert sein kann oder nicht (mit oder ohne Abruf) Ich führe einen Test durch, bevor er von Hibernate initialisiert wurde, und füge die Anmerkung hinzu, die das Serialisieren einer leeren Eigenschaft verhindert und mein Problem gelöst hat.
quelle
Ich habe den ganzen Tag versucht, das gleiche Problem zu lösen. Sie können dies tun, ohne die vorhandene Konfiguration der Nachrichtenkonverter zu ändern.
Meiner Meinung nach ist der einfachste Weg, dieses Problem nur mit 2 Schritten mit Hilfe von jackson-datatype-hibernate zu lösen :
build.gradle.kts
:implementation("com.fasterxml.jackson.datatype:jackson-datatype-hibernate5:$jacksonHibernate")
@Bean
@Bean fun hibernate5Module(): Module = Hibernate5Module()
Beachten Sie, dass dies nicht der Fall
Module
istcom.fasterxml.jackson.databind.Module
java.util.Module
Es empfiehlt sich auch,
@JsonBackReference
&@JsonManagedReference
zu@OneToMany
&@ManyToOne
Beziehungen hinzuzufügen .@JsonBackReference
könnte nur 1 in der Klasse sein.quelle
Obwohl sich diese Frage geringfügig von der folgenden unterscheidet: Eine seltsame Jackson-Ausnahme, die beim Serialisieren des Hibernate-Objekts ausgelöst wird , kann das zugrunde liegende Problem auf dieselbe Weise mit diesem Code behoben werden:
@Provider public class MyJacksonJsonProvider extends JacksonJsonProvider { public MyJacksonJsonProvider() { ObjectMapper mapper = new ObjectMapper(); mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); setMapper(mapper); } }
quelle
Ich habe es versucht und gearbeitet:
// benutzerdefinierte Konfiguration für verzögertes Laden
public static class HibernateLazyInitializerSerializer extends JsonSerializer<JavassistLazyInitializer> { @Override public void serialize(JavassistLazyInitializer initializer, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException { jsonGenerator.writeNull(); } }
und Mapper konfigurieren:
mapper = new JacksonMapper(); SimpleModule simpleModule = new SimpleModule( "SimpleModule", new Version(1,0,0,null) ); simpleModule.addSerializer( JavassistLazyInitializer.class, new HibernateLazyInitializerSerializer() ); mapper.registerModule(simpleModule);
quelle
Eine weitere Lösung für Spring Boot: Konfiguration:
spring.jpa.open-in-view=true
quelle
Ich habe die nützliche Antwort von @ rick ausprobiert, bin jedoch auf das Problem gestoßen, dass "bekannte Module" wie jackson-datatype-jsr310 nicht automatisch registriert wurden, obwohl sie sich im Klassenpfad befanden. (Dieser Blog-Beitrag erklärt die automatische Registrierung.)
Um die Antwort von @ rick zu erweitern, sehen Sie hier eine Variation, bei der Spring's Jackson2ObjectMapperBuilder zum Erstellen des
ObjectMapper
. Dadurch werden die "bekannten Module" automatisch registriert und zusätzlich zur Installation der Funktionen bestimmte Funktionen festgelegtHibernate4Module
.@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() .modulesToInstall(hibernate4Module()); return new MappingJackson2HttpMessageConverter(builder.build()); } @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { converters.add(jacksonMessageConverter()); super.configureMessageConverters(converters); } }
quelle
hibernate5Module
(weil ich Ruhezustand 5 verwende), ignoriert Jackson den Proxy von nicht initialisierten Objekten, aber es scheint, dass die "bekannten Module" nicht registriert werden, obwohl ich Ihren Ansatz verwende. Ich weiß das, weil einige Teile meiner Anwendung fehlerhaft sind und wenn ich das Modul entferne, funktionieren sie wieder.DelegatingWebMvcConfiguration
Klasse (anstelle vonWebMvcConfigurerAdapter
), wie vom Jackson-Antpath-Filter vorgeschlagen :@Override public void configureMessageConverters(List<HttpMessageConverter<?>> messageConverters) { messageConverters.add(jacksonMessageConverter()); addDefaultHttpMessageConverters(messageConverters); }
Hibernate4Module
ist Vermächtnis. 2SerializationFeature.WRITE_DATES_AS_TIMESTAMPS
- nicht über die Frage