Userオブジェクトを返す単純なコントローラーがあります。このユーザーには、hibernateプロパティFetchType.LAZYを持つ属性座標があります。
このユーザーを取得しようとすると、常にすべての座標を読み込んでユーザーオブジェクトを取得する必要があります。そうしないと、ジャクソンがユーザーをシリアル化しようとすると例外がスローされます。
com.fasterxml.jackson.databind.JsonMappingException:プロキシを初期化できませんでした-セッションなし
これは、ジャクソンがこの未取得オブジェクトを取得しようとしているためです。オブジェクトは次のとおりです。
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;
}
そしてコントローラー:
@RequestMapping(value = "/user/{username}", method=RequestMethod.GET)
public @ResponseBody User getUser(@PathVariable String username) {
User user = userService.getUser(username);
return user;
}
フェッチされていないオブジェクトをシリアル化しないようにジャクソンに伝える方法はありますか?私は3年前にjackson-hibernate-moduleを実装する他の回答を探してきました。しかし、おそらくそれは新しいジャクソン機能で達成できるでしょう。
私のバージョンは:
前もって感謝します。
私はついに解決策を見つけました!手がかりを与えてくれたindybeeに感謝します。
チュートリアル Spring 3.1、Hibernate 4およびJackson-Module-Hibernate には、Spring 3.1以前のバージョンに適したソリューションがあります。ただし、バージョン3.1.2 Springには独自のMappingJackson2HttpMessageConverterがあり、チュートリアルの機能とほぼ同じ機能を備えているため、これを作成する必要はありません。カスタムHTTPMessageConverter。
Javaconfigを使用すると、HibernateAwareObjectMapperも作成する必要がなく、Hibernate4ModuleSpringが既に持っているデフォルトMappingJackson2HttpMessageConverterに追加し、アプリケーションのHttpMessageConvertersに追加します。
WebMvcConfigurerAdapterからスプリング設定クラスを拡張し、メソッドconfigureMessageConvertersをオーバーライドします。
そのメソッドで、MappingJackson2HttpMessageConverterとpreviusメソッドで登録されたHibernate4Moduleを追加します。
構成クラスは次のようになります。
@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....
}
Xml構成がある場合、独自のMappingJackson2HttpMessageConverterも作成する必要はありませんが、チュートリアルに表示されるパーソナライズされたマッパー(HibernateAwareObjectMapper)を作成する必要があるため、xml構成は次のようになります。
<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>
この回答が理解可能であり、誰かがこの問題の解決策を見つけるのに役立つことを願っています。どんな質問でもお気軽に!
Spring 4.2以降、Spring Bootとjavaconfigを使用して、Hibernate4Moduleの登録は、これを構成に追加するのと同じくらい簡単になりました。
@Bean
public Module datatypeHibernateModule() {
return new Hibernate4Module();
}
参照: https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring
これは、@ rickが承認したソリューションに似ています。
既存のメッセージコンバーターの構成を変更したくない場合は、次のようにJackson2ObjectMapperBuilder
Beanを宣言するだけです。
@Bean
public Jackson2ObjectMapperBuilder configureObjectMapper() {
return new Jackson2ObjectMapperBuilder()
.modulesToInstall(Hibernate4Module.class);
}
Gradleファイル(またはMaven)に次の依存関係を追加することを忘れないでください。
compile 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate4:2.4.4'
spring-boot アプリケーションがあり、application.properties
ファイルからJacksonの機能を変更する機能を保持したい場合に便利です。
Spring Data Restの場合、@ r1ckrによって投稿されたソリューションは機能しますが、Hibernateのバージョンに応じて次の依存関係のいずれかを追加するだけです。
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate4</artifactId>
<version>${jackson.version}</version>
</dependency>
または
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate5</artifactId>
<version>${jackson.version}</version>
</dependency>
Spring Data Restには次のクラスがあります。
org.springframework.data.rest.webmvc.json.Jackson2DatatypeHelper
これにより、アプリケーションの起動時にモジュールが自動検出および登録されます。
ただし、問題があります。
XML構成を使用し、<annotation-driven />
を使用する場合、 Jackson Githubアカウント で推奨されているように、<message-converters>
内に<annotation-driven>
をネストする必要があることがわかりました。
そのようです:
<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>
`
Apache CXFベースのRESTfulサービスのソリューションを探してここに来た人のために、それを修正する構成は以下のとおりです。
<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"/>
HibernateAwareObjectMapperは次のように定義されます。
public class HibernateAwareObjectMapper extends ObjectMapper {
public HibernateAwareObjectMapper() {
registerModule(new Hibernate5Module());
}
}
2016年6月現在、次の依存関係が必要です(Hibernate5を使用している場合):
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate5</artifactId>
<version>2.7.4</version>
</dependency>
次のソリューションは、パフォーマンス上の理由から、すべてのフェッチタイプをfetch=FetchType.LAZY
の場合からfetch=FetchType.EAGER
に移行したSpring 4.3、(非ブート)およびHibernate 5.1に対するものです。すぐに、遅延ロードの問題によるcom.fasterxml.jackson.databind.JsonMappingException: could not initialize proxy
例外が見つかりました。
最初に、次のMaven依存関係を追加します。
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate5</artifactId>
</dependency>
次に、以下がJava MVC構成ファイルに追加されます。
@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;
}
ノート:
Hibernate5Moduleを作成および構成して、このモジュールなしでJacksonに似た動作を取得する必要があります。デフォルトでは、互換性のない仮定が行われます。
WebMvcConfigurerAdapter
には他にも多くの設定があり、別の設定クラスを避けたかったので、他の投稿で参照されていたWebMvcConfigurationSupport#addDefaultHttpMessageConverters
関数を使用しませんでした。
WebMvcConfigurerAdapter#configureMessageConverters
は、Springのすべてのメッセージコンバーターの内部構成を無効にします。これに関する潜在的な問題を回避することをお勧めします。
extendMessageConverters
を使用すると、他のすべてのメッセージコンバーターの構成を失うことなく、自動的に構成されたすべてのJacksonクラスへのアクセスが可能になりました。
getObjectMapper#registerModule
を使用して、Hibernate5Module
を既存のコンバーターに追加できました。
この追加により、Hibernateと遅延読み込みの問題は解決しましたが、生成されたJSON形式の残留問題が発生しました。 このgithubの問題 で報告されているように、hibernate-jacksonの遅延ロードモジュールは現在@JsonUnwrappedアノテーションを無視し、潜在的なデータエラーを引き起こします。これは、強制ロード機能の設定に関係なく発生します。この問題は2016年から続いています。
注
遅延ロードされるクラスに以下を追加することにより、組み込みObjectMapperはhibernate5モジュールを追加せずに動作するようです。
@JsonIgnoreProperties( {"handler","hibernateLazyInitializer"} )
public class Anyclass {
Spring Data Restプロジェクトから次のヘルパーを使用できます。
Jackson2DatatypeHelper.configureObjectMapper(objectMapper);
この質問はこれとは少し異なりますが、 Hibernateオブジェクトのシリアル化中にストレンジジャクソンの例外がスローされます ですが、根本的な問題はこのコードで同じ方法で修正できます。
@Provider
public class MyJacksonJsonProvider extends JacksonJsonProvider {
public MyJacksonJsonProvider() {
ObjectMapper mapper = new ObjectMapper();
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
setMapper(mapper);
}
}
この問題を非常に簡単に解決しました。
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public Set<Pendency> getPendencies() {
return Hibernate.isInitialized(this.pendencies) ? Collections.unmodifiableSet(this.pendencies) : new HashSet<>();
}
私の場合、エラーを与えていました。ペンダントを返すたびに、良い習慣としてそれを変更できないリストに変換しましたが、インスタンスを取得するために使用した方法に応じてそれがどのように遅延する可能性があるか(フェッチの有無にかかわらず)、Hibernateによって初期化される前にテストを実行し、空のプロパティのシリアル化を妨げ、問題を解決する注釈を追加します。
@ rickの便利な答え を試しましたが、 jackson-datatype-jsr31 などの「よく知られたモジュール」が、クラスパス。 (この ブログ投稿 は自動登録について説明しています。)
@rickの答えを拡張すると、Springの Jackson2ObjectMapperBuilder を使用してObjectMapper
を作成するバリエーションがあります。これにより、「既知のモジュール」が自動登録され、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);
}
}
私はこれを試して働いた:
//遅延読み込みのカスタム構成
public static class HibernateLazyInitializerSerializer extends JsonSerializer<JavassistLazyInitializer> {
@Override
public void serialize(JavassistLazyInitializer initializer, JsonGenerator jsonGenerator,
SerializerProvider serializerProvider)
throws IOException, JsonProcessingException {
jsonGenerator.writeNull();
}
}
マッパーを構成します。
mapper = new JacksonMapper();
SimpleModule simpleModule = new SimpleModule(
"SimpleModule", new Version(1,0,0,null)
);
simpleModule.addSerializer(
JavassistLazyInitializer.class,
new HibernateLazyInitializerSerializer()
);
mapper.registerModule(simpleModule);