フェッチされていないレイジーオブジェクトでのJacksonシリアル化を回避する


83

Userオブジェクトを返す単純なコントローラーがあります。このユーザーには、休止状態のプロパティFetchType.LAZYを持つ属性座標があります。

このユーザーを取得しようとすると、ユーザーオブジェクトを取得するために常にすべての座標をロードする必要があります。そうしないと、Jacksonがユーザーをシリアル化しようとすると例外がスローされます。

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;

}

フェッチされていないオブジェクトをシリアル化しないようにJacksonに指示する方法はありますか?私は3年前に投稿された他の回答を探していました。jackson-hibernate-moduleを実装しています。しかし、おそらくそれは新しいジャクソン機能で達成できるでしょう。

私のバージョンは次のとおりです。

  • 春3.2.5
  • Hibernate 4.1.7
  • ジャクソン2.2

前もって感謝します。


3
これらの2つのリンクで説明されているアプローチは、Spring 3.1、Hibernate 4、Jackson-Module-Hibernate およびFasterXML / jackson-datatype-hibernate
indybee 2014

indybeeに感謝します、私はチュートリアルを見てきました、春3.2.5にはすでにMappingJackson2HttpMessageConverterがありますが、フェッチされていない怠惰なオブジェクトを回避するためにそれを使用することはできません。また、チュートリアルで実装しようとしましたが、何もしませんでした。 ..
r1ckr 2014

1
「プロキシを初期化できませんでした」という同じエラーが発生しますか、それとも別のエラーが発生しますか?(
Spring3.2.6

1
とった!!バージョン3.1.2以降、Springには独自のMappingJackson2HttpMessageConverterがあり、チュートリアルの動作とほぼ同じですが、問題は、Spring java configを使用していて、javaconfigsから始めていることでした。私は@ BeanMappingJackson2HttpMessageConverterを作成しようとしていましたが、SpringのHttpMessageConvertersに追加するのではなく、答えの方が明確かもしれません:)
r1ckr 2014

それが機能していることをうれしく思います:)
indybee 2014

回答:


93

私はついに解決策を見つけました!手がかりを与えてくれたindybeeに感謝します。

チュートリアルSpring3.1、Hibernate 4、およびJackson-Module-Hibernateには、Spring3.1以前のバージョンに適したソリューションがあります。ただし、バージョン3.1.2以降、Springにはチュートリアルとほぼ同じ機能を持つ独自のMappingJackson2HttpMessageConverterがあるため、このカスタムHTTPMessageConverterを作成する必要はありません。

javaconfigを使用すると、HibernateAwareObjectMapperも作成する必要はありません。SpringにすでにあるデフォルトのMappingJackson2HttpMessageConverterにHibernate4Moduleを追加し、それをアプリケーションのHttpMessageConvertersに追加するだけです。

  1. Spring configクラスをWebMvcConfigurerAdapterから拡張し、メソッドconfigureMessageConvertersをオーバーライドします

  2. そのメソッドで、前のメソッドに登録されたHibernate4Moduleを使用してMappingJackson2HttpMessageConverterを追加します。

構成クラスは次のようになります。

@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>

この回答が理解可能であり、誰かがこの問題の解決策を見つけるのに役立つことを願っています。質問がある場合は、遠慮なく質問してください。


3
ソリューションを共有していただきありがとうございます。しかし、数回試した後、現時点での最新のジャクソン(2.4.2)はSpring4.0.6では機能しないことがわかりました。まだ「セッションなし」エラーがあります。ジャクソンバージョンを2.3.4(または2.4.2)にダウングレードしたときにのみ、機能し始めました。
ロビン

1
MappingJackson2HttpMessageConverterを指摘していただきありがとうございます。しばらくこのようなクラスを探していました。
LanceP 2015年

1
どうもありがとうございました!私はそれをテストし、Spring 4.2.4、Jackson 2.7.1-1、JacksonDatatype 2.7.1で動作しました:)
Isthar 2016

1
これはSpringDataRESTの問題を解決しません。
アレッサンドロC

1
Springの代わりにSpringbootを使用した場合、同じ問題を解決するにはどうすればよいですか?Hibernate4モジュール(現在のバージョン)を登録しようとしましたが、問題は解決しませんでした。
ranjithGampa18年

34

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


19
Hibernate 5の場合(現在のバージョンのSpring Bootと同様)はHibernate5Moduleです。さらに、依存関係が必要です<groupId>com.fasterxml.jackson.datatype</groupId><artifactId>jackson-datatype-hibernate5</artifactId>
Zeemee 2017年

1
Spring Boot2.1でも私のために働いた。私を夢中にさせ始めていました。
ヴァンダム

これを実装する方法は明確ではありません。互換性のないタイプのエラーが発生します。
EarthMind 2018

2
驚くばかり!春ブーツ2.0.6およびHibernateは5で私のために働いた
GarouDan

これはとても役に立ちました。Hibernate5ModuleクラスインスタンスをBeanとして登録するだけでは不十分であることに注意してください。これを単一のObjectMapperインスタンスに適用する場合は、モジュールをSpring管理クラスに自動配線してから、ObjectMapperインスタンスに登録できます。@AutowireモジュールjacksonHibernateModule; ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(this.jacksonHibernateModule)。
gburgalum 0119年

12

これは、@ 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'

あなたが持っている場合に便利 アプリケーションとあなたはapplication.propertiesファイルからジャクソンの機能を変更する機能を維持したいです。


application.propertiesファイルを使用してこれをどのように達成できますか(遅延ロードされたオブジェクトを回避するようにJacksonを構成する)?
hemu 2016年

1
それはここで言われていることではありません。@Luis Bellochのソリューションでは、Jackson2ObjectMapperBuilderを作成して、Spring Bootsのデフォルトのメッセージコンバーターをオーバーライドせず、引き続きapplication.properties値を使用してjacksonプロパティを制御することをお勧めします。
kapad 2018年

申し訳ありません...私は新しいですこのメソッドをどこに実装しますか?SpringBootを使用するWebMvcConfigurerAdapterが非推奨となったSpring5を使用
Eric Huang

Hibernate4Moduleレガシーです
Dmitry Kaltovich

8

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

これは、アプリケーションの起動時にモジュールを自動検出して登録します。

ただし、問題があります。

レイジー@ManyToOneのシリアル化の問題


Spring Data Restを使用していない場合、Spring Bootでこれをどのように構成しますか?
エリック・黄

十分ではない。また必要です@Bean hibernate5Module()
Dmitry Kaltovich

4

XML構成を使用してを使用する場合、JacksonGithubアカウントで推奨されているように内部<annotation-driven />にネストする必要があることがわかりました。<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>

`


xml構成はレガシーです
Dmitry Kaltovich

3

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>  

1

次の解決策は、Spring 4.3(非ブート)およびHibernate 5.1用でfetch=FetchType.LAZYありfetch=FetchType.EAGER、パフォーマンス上の理由から、すべてのフェッチタイプをケースからに移行しました。すぐcom.fasterxml.jackson.databind.JsonMappingException: could not initialize proxyに、遅延読み込みの問題による例外が発生しました。

まず、次のMaven依存関係を追加します。

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

次に、JavaMVC構成ファイルに以下が追加されます。

@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;
}

ノート:

  • このモジュールなしでJacksonと同様の動作を得るには、Hibernate5Moduleを作成して構成する必要があります。デフォルトでは、互換性のない仮定が行われます。

  • 私たちはWebMvcConfigurerAdapterそれに他の構成をたくさん持っているし、我々は我々が使用しなかった理由である別の構成クラス、避けたかったWebMvcConfigurationSupport#addDefaultHttpMessageConvertersの他の記事で言及された機能を。

  • WebMvcConfigurerAdapter#configureMessageConvertersSpringのメッセージコンバーターの内部構成をすべて無効にします。これに関する潜在的な問題を回避することをお勧めします。

  • extendMessageConverters他のすべてのメッセージコンバーターの構成を失うことなく、自動的に構成されたすべてのJacksonクラスへの有効なアクセスを使用します。

  • を使用getObjectMapper#registerModuleHibernate5Moduleて、既存のコンバーターにを追加することができました。

  • モジュールはJSONプロセッサとXMLプロセッサの両方に追加されました

この追加により、Hibernateと遅延読み込みの問題は解決されましたが、生成されたJSON形式で問題が残りましたこのgithubの問題で報告されているように、hibernate-jackson遅延ロードモジュールは現在@JsonUnwrappedアノテーションを無視しているため、潜在的なデータエラーが発生します。これは、強制荷重機能の設定に関係なく発生します。問題は2016年以来続いています。


注意

遅延ロードされるクラスに以下を追加することにより、組み込みのObjectMapperはhibernate5モジュールを追加しなくても機能するようです。

@JsonIgnoreProperties(  {"handler","hibernateLazyInitializer"} )
public class Anyclass {

解決策ははるかに簡単かもしれません: kotlin @Bean fun hibernate5Module(): Module = Hibernate5Module()
Dmitry Kaltovich

0

Spring DataRestプロジェクトから次のヘルパーを使用できます。

Jackson2DatatypeHelper.configureObjectMapper(objectMapper);

質問についてではありません
Dmitry Kaltovich

0

私はこの問題に対して非常に簡単な解決策を作成しました。

@JsonInclude(JsonInclude.Include.NON_EMPTY)
public Set<Pendency> getPendencies() {
    return Hibernate.isInitialized(this.pendencies) ? Collections.unmodifiableSet(this.pendencies) : new HashSet<>();
}

私の場合、ペンダントを返すたびに、変更できないリストに変換したため、エラーが発生していましたが、インスタンスを取得するために使用した方法に応じて、どのようにレイジーになるかどうかが決まりました。 (フェッチの有無にかかわらず)、Hibernateによって初期化される前にテストを実行し、空のプロパティのシリアル化を防ぎ、問題を解決するアノテーションを追加します。


0

私は同じ問題を解決するために一日を費やしました。既存のメッセージコンバーターの構成を変更せずにそれを行うことができます。

私の意見では、jackson-datatype-hibernateの助けを借りて2つのステップだけでこの問題を解決する最も簡単な方法:

kotlinの例(javaと同じ):

  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@JsonManagedReferenceto @OneToMany@ManyToOneリレーションシップを追加することもお勧めします。@JsonBackReferenceクラスで1つだけである可能性があります。


-1

この質問はこれとは少し異なります:Hibernateオブジェクトをシリアル化するときにスローされる奇妙なジャクソン例外ですが、根本的な問題はこのコードで同じ方法で修正できます:

@Provider
public class MyJacksonJsonProvider extends JacksonJsonProvider {
    public MyJacksonJsonProvider() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        setMapper(mapper);
    }
}

-1

私はこれを試し、動作しました:

//遅延読み込みのカスタム構成

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);


-1

@rickの便利な答えを試しましたが、jackson-datatype-jsr310などの「既知のモジュール」がクラスパスにあるにもかかわらず自動的に登録されないという問題が発生しました。(このブログ投稿では、自動登録について説明しています。)

@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()
                            .modulesToInstall(hibernate4Module());
        return new MappingJackson2HttpMessageConverter(builder.build());
  }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(jacksonMessageConverter());
        super.configureMessageConverters(converters);
    }
}

このトピックに関する最新情報はありますか?現在問題が発生しています。もちろん、hibernate5Module(imはhibernate 5を使用しているため)追加すると、jacksonは初期化されていないオブジェクトのプロキシを無視しますが、あなたのアプローチを使用しているにもかかわらず、「既知のモジュール」が登録されていないようです。私のアプリケーションのいくつかの部分が壊れているので、私はこれを知っています、そして私がモジュールを取り除くとき、それらは再び働きます。
ダニエルヘナオ

@DanielHenaospring.io/blog/2014/12/02/…を勉強しましたか?Jackson-Antpath-Filterによって提案されたDelegatingWebMvcConfigurationクラス(の代わりにWebMvcConfigurerAdapter)を使用して、Hibernate5に切り替えました:@Override public void configureMessageConverters(List<HttpMessageConverter<?>> messageConverters) { messageConverters.add(jacksonMessageConverter()); addDefaultHttpMessageConverters(messageConverters); }
MarkusPscheidt18年

1.Hibernate4Moduleレガシーです。2-SerializationFeature.WRITE_DATES_AS_TIMESTAMPS質問についてではありません
Dmitry Kaltovich

@DmitryKaltovich ad 1)これを書いている時点ではレガシーではありませんでした。広告2)わかりました、削除されました。
Markus Pscheidt
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.