クラスorg.hibernate.proxy.pojo.javassist.Javassistのシリアライザーが見つかりませんか?


94

私が働いているSpringMVCHibernateJSON私はこのエラーを取得しています。

HTTP Status 500 - Could not write JSON: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationConfig.SerializationFeature.FAIL_ON_EMPTY_BEANS) ) 

以下のエンティティを確認してください

    @Entity
@Table(name="USERS")
public class User {

    @Id
    @GeneratedValue
    @Column(name="USER_ID")
    private Integer userId;

    @Column(name="USER_FIRST_NAME")
    private String firstName;

    @Column(name="USER_LAST_NAME")
    private String lastName;


    @Column(name="USER_MIDDLE_NAME")
    private String middleName;

    @Column(name="USER_EMAIL_ID")
    private String emailId;

    @Column(name="USER_PHONE_NO")
    private Integer phoneNo;

    @Column(name="USER_PASSWORD")
    private String password;

    @Column(name="USER_CONF_PASSWORD")
    private String  confPassword;

    @Transient
    private String token;

    @Column(name="USER_CREATED_ON")
    private Date createdOn;

    @OneToMany(fetch=FetchType.EAGER,cascade=CascadeType.ALL)
    @Fetch(value = FetchMode.SUBSELECT)
    @JoinTable(name = "USER_ROLES", joinColumns = { @JoinColumn(name = "USER_ID") }, inverseJoinColumns = { @JoinColumn(name = "ROLE_ID") })
    private List<ActifioRoles> userRole = new ArrayList<ActifioRoles>();


    @OneToMany(fetch=FetchType.EAGER,cascade=CascadeType.ALL,mappedBy="userDetails")
    @Fetch(value = FetchMode.SUBSELECT)
    private List<com.actifio.domain.Address> userAddress = new ArrayList<com.actifio.domain.Address>();

    @OneToOne(cascade=CascadeType.ALL)
    private Tenant tenantDetails;


    public Integer getUserId() {
        return userId;
    }
    public void setUserId(Integer userId) {
        this.userId = userId;
    }
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public String getEmailId() {
        return emailId;
    }
    public void setEmailId(String emailId) {
        this.emailId = emailId;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getConfPassword() {
        return confPassword;
    }
    public void setConfPassword(String confPassword) {
        this.confPassword = confPassword;
    }
    public Date getCreatedOn() {
        return createdOn;
    }
    public void setCreatedOn(Date createdOn) {
        this.createdOn = createdOn;
    }

    public List<ActifioRoles> getUserRole() {
        return userRole;
    }

    public void setUserRole(List<ActifioRoles> userRole) {
        this.userRole = userRole;
    }
    public String getMiddleName() {
        return middleName;
    }
    public void setMiddleName(String middleName) {
        this.middleName = middleName;
    }
    public Integer getPhoneNo() {
        return phoneNo;
    }
    public void setPhoneNo(Integer phoneNo) {
        this.phoneNo = phoneNo;
    }

    public List<com.actifio.domain.Address> getUserAddress() {
        return userAddress;
    }
    public void setUserAddress(List<com.actifio.domain.Address> userAddress) {
        this.userAddress = userAddress;
    }
    public Tenant getTenantDetails() {
        return tenantDetails;
    }
    public void setTenantDetails(Tenant tenantDetails) {
        this.tenantDetails = tenantDetails;
    }
    public String getToken() {
        return token;
    }
    public void setToken(String token) {
        this.token = token;
    }

    }

どうすればこれを解決できますか?


スタックトレースと例外が発生するコードを表示してください
geoおよび

コードが何をしようとしているのかについての知識がなければ、デバッグは少し難しいですが、JacksonとHibernateを使用しているので、
github.com / FasterXML / jackson

このクラスからJSONを作成しようとしていますか?この場合、JSONシリアライザーはすべてのプロパティと、多対多の関係のHashSetを書き込もうとします。これにより、遅延初期化例外が発生します
Angelo Immediata 14


@ user2963481 ...素晴らしくて非常に役立つ質問Bro。

回答:


192

hibernateプロキシオブジェクトを介した遅延読み込みで同様の問題が発生しました。遅延ロードされたプライベートプロパティを持つクラスに次のアノテーションを付けることで、問題を回避できます。

@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})

JSONシリアル化をそのアノテーションに解除するプロパティをプロキシオブジェクトに追加できると思います。

問題は、エンティティが遅延して読み込まれ、完全に読み込まれる前にシリアル化が行われることです。

Hibernate.initialize(<your getter method>);

3
それも私のために働いた...この奇妙な行動に何か説明はありますか?
ビクター

7
@ ankur-singhalでは、このアノテーションはネストされたクラスで必要であり、呼び出しクラスでは必要ないことを強調してください。
ダーウェイン2017年

1
Yahooo ...それはうまくいった。何度か試してみました。私はいくつかの方法とBoooMを試しました。働いた。
ブレイン

2
私にも感謝しました。このアノテーションは、遅延読み込みされたBeanを持つ他のエンティティにアクセスするエンティティに追加する必要があります。
Shafqat Shafi 2018

3
これは問題の正しい解決策ではないと思います。シリアル化するときは、完全なサブオブジェクト、または少なくともサブオブジェクトの主キーが結果で返されることが期待されます。これらの注釈を追加しても、エラーは抑制されますが、望ましい結果は得られません。
Anand Vaidya

84

これを追加するだけで、同じ問題に遭遇しましたが、提供された回答が機能しませんでした。例外の提案を取り、application.propertiesファイルに追加することで修正しました...

spring.jackson.serialization.fail-on-empty-beans=false

Hibernate 4.3でSpring Boot v1.3を使用しています

これで、オブジェクト全体とネストされたオブジェクトがシリアル化されます。

編集:2018

これはまだコメントを受け取るので、ここで明確にします。これは絶対にエラーを隠すだけです。パフォーマンスへの影響があります。当時、私はそれを提供し、後で作業するために何かが必要でした(これは、もうSpringを使用しませんでした)。ですから、本当に問題を解決したいのであれば、誰かに聞いてください。とりあえずやりたいだけなら、この答えを使ってください。それはひどい考えですが、一体、うまくいくかもしれません。ちなみに、これ以降、クラッシュや問題が発生することはありませんでした。しかし、それはおそらくSQLパフォーマンスの悪夢に終わった原因です。


8
問題を解決しますが、jsonには2つの余分な不要なプロパティが含まれます"handler":{},"hibernateLazyInitializer":{}
prettyvoid

すごい!ここでも修正、これが答えです
Hinotori

@prettyvoidなぜその余分なプロパティが存在するのですか?
Robert Moon

12
@RobertMoonそれらを削除したい場合は、エンティティに注釈を付けることができます@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
prettyvoid

@prettyvoidありがとう。別の解決策は、LAZYをEAGERに変更することです。パフォーマンスに影響はありますか?
Robert Moon

68

以前の回答で正しく提案されているように、遅延読み込みとは、データベースからオブジェクトをフェッチするときに、ネストされたオブジェクトがフェッチされないことを意味します(必要に応じて後でフェッチされる場合もあります)。

現在、Jacksonはネストされたオブジェクトをシリアル化しようとします(== JSONを作成します)が、通常のオブジェクトではなくJavassistLazyInitializerを見つけるため、失敗します。これは表示されるエラーです。それを解決するにはどうすればよいですか?

以前のCP510で提案されているように、1つのオプションは、この構成の行によってエラーを抑制することです。

spring.jackson.serialization.fail-on-empty-beans=false

しかし、これは症状ではなく原因を扱っています。エレガントに解決するには、このオブジェクトがJSONで必要かどうかを判断する必要がありますか?

  1. JSONでオブジェクトが必要な場合は、FetchType.LAZYそれを引き起こすフィールドからオプションを削除します(フェッチするルートエンティティだけでなく、ネストされたオブジェクトのフィールドである可能性もあります)。

  2. JSONでオブジェクトが必要ない場合は、このフィールドのゲッター(または、入力値を受け入れる必要がない場合はフィールド自体)に、次のように注釈を付けます@JsonIgnore

    // this field will not be serialized to/from JSON @JsonIgnore private NestedType secret;

より複雑なニーズがある場合(たとえば、同じエンティティを使用する異なるRESTコントローラーの異なるルール)、ジャクソンビューまたはフィルタリングを使用するか、非常に単純な使用例では、ネストされたオブジェクトを個別にフェッチできます。


15
これが最良の答えです。問題の根本に対処し、次のプログラマが対処する必要がある症状を隠すだけではありません。
Andrew

2
あなたは私のヒーローです。
Leansrosレイヤンス

エラーの背後にある理由を説明する2つの溶液を提供するためにどうもありがとうございましたそのメイクセンス
マウリシオPoppe

これをありがとう、しかし、springfox- RangeError: Maximum call stack size exceeded
swaggerがまだ

18

Hibernateの遅延読み込みを処理するJacksonのアドオンモジュールを使用できます。

https://github.com/FasterXML/jackson-datatype-hibernateの詳細は、Hibernate 3および4を個別にサポートしています


7
これは本当に正しい答えです。他のすべての答えは、問題を解決するのではなく、隠れています。
Ahmed Hassanien 2017

しかし、このモジュールの使い方は?それのためのガイドはありますか?
Anand Vaidya、

jackson-datatype-hibernateを構成する方法:stackoverflow.com/q/33727017
追跡

13

問題は、エンティティを取得する方法にあると思います。

多分あなたはこのようなことをしています:

Person p = (Person) session.load(Person.class, new Integer(id));

get代わりにメソッドを使用してみてくださいload

Person p = (Person) session.get(Person.class, new Integer(id));

問題は、loadメソッドを使用すると、プロキシのみが取得され、実際のオブジェクトは取得されないことです。プロキシオブジェクトにはプロパティが既に読み込まれていないため、シリアル化が行われるときにシリアル化するプロパティはありません。getメソッドを使用すると、実際に実際のオブジェクトを取得できますが、このオブジェクトは実際にはシリアル化できます。


2
findOneの代わりにgetOneを使用したときにも、非常によく似たエラーが発生しました。私の場合、インターフェースリポジトリはJpaRepositoryを拡張していました。findOneは、Jsonアノテーションやapplication.propertiesの変更なしでうまく機能しました
James Freitas

James Freitasと同様に、getOneが問題の原因であることがわかりました。代わりに、たとえばfindByIdを使用すると、アプリケーションのプロパティで注釈や上記の行を使用する必要なく問題を解決できました
MY

1
私の同僚は私を明確にし、「基本的な違いはgetOneが遅延ロードされ、findOneがそうでないことです」-明らかに前者は実際にはデータベースからデータ行を取得せず、参照を作成するだけであることを意味しました。後者は実際には行を取得しますが、これはしばしば望まれるケースです
MY

@JamesFreitasはい!ありがとうございました!そして、ジェームズをこの素晴らしい結論に導いてくれたカルロスに感謝します!
フィリップサビッチ

9

わたしにはできる

@JsonIgnoreProperties({"hibernateLazyInitializer","handler"})

例えば

@Entity
@Table(name = "user")
@Data
@NoArgsConstructor
@JsonIgnoreProperties({"hibernateLazyInitializer","handler"})
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private Date created;

}

5

問題を解決するには2つの方法があります。

方法1

追加 spring.jackson.serialization.fail-on-empty-beans=falseapplication.properties

方法2

join fetchJPQLクエリで使用して親オブジェクトデータを取得します。以下を参照してください。

@Query(value = "select child from Child child join fetch child.parent Parent ",
           countQuery = "select count(*) from Child child join child.parent parent ")
public Page<Parent> findAll(Pageable pageable); 

ありがとう!それは素晴らしい最初の解決策です!
Lucas Moyano Angelini

方法1.すばらしい。ありがとうございました。
Leansrosレイ

3

この注釈をエンティティクラス(モデル)に追加すると、Hibernateプロキシオブジェクトを介して遅延読み込みが発生します。

@JsonIgnoreProperties({"hibernateLazyInitializer"、 "handler"})


2

この例外

org.springframework.http.converter.HttpMessageNotWritableException

なぜなら、あなたが応答出力をSerializableオブジェクトとして送信しているからです。
これは春に発生する問題です。この問題を解決するには、POJOオブジェクトを応答出力として送信します。

例:

    @Entity
    @Table(name="user_details")
    public class User implements Serializable{

        @Id
        @GeneratedValue(strategy= GenerationType.IDENTITY)
        @Column(name="id")
        private Integer id;

        @Column(name="user_name")
        private String userName;

        @Column(name="email_id")
        private String emailId;

        @Column(name="phone_no")
        private String phone;

//setter and getters

POJOクラス:

public class UserVO {

    private int Id;
    private String userName;
    private String emailId;
    private String phone;
    private Integer active;

//setter and getters

コントローラーで、シリアライズ可能なオブジェクトフィールドをPOJOクラスフィールドに変換し、出力としてpojoクラスを返します。

         User u= userService.getdetials(); // get data from database

        UserVO userVo= new UserVO();  // created pojo class object

        userVo.setId(u.getId());
        userVo.setEmailId(u.getEmailId());
        userVo.setActive(u.getActive());
        userVo.setPhone(u.getPhone());
        userVo.setUserName(u.getUserName());
       retunr userVo;  //finally send pojo object as output.

はい私は同じことをしていますが、同じことをしても、同じエラーが発生します。エンティティクラスを設定するだけのオブジェクト関連データを送信しているからです
Pra_A

2

Hibernate 5.2以降では、以下のようにhibernateプロキシを削除できます。これにより、実際のオブジェクトが提供され、適切にシリアル化できます。

Object unproxiedEntity = Hibernate.unproxy( proxy );

Hibernate.initialize事前に自動的に呼び出されます。
GMsoF 2018

@ Tim、jsonシリアライゼーションの前に追加する必要があります。この問題は、シリアル化中に休止状態のプロキシオブジェクトが見つかったために発生するため、いったん削除すると、シリアル化は正常に行われます。スプリングコントローラを使用している場合は、コントローラが終了する前に実行する必要があります。
GMsoF 2018

1

Hibernateの場合、jackson-datatype-hibernateプロジェクトを使用して、遅延ロードされたオブジェクトによるJSONシリアル化/非シリアル化に対応できます。

例えば、

import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JacksonDatatypeHibernate5Configuration {

    // Register Jackson Hibernate5 Module to handle JSON serialization of lazy-loaded entities
    // Any beans of type com.fasterxml.jackson.databind.Module are automatically
    // registered with the auto-configured Jackson2ObjectMapperBuilder
    // https://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-mvc.html#howto-customize-the-jackson-objectmapper
    @Bean
    public Module hibernate5Module() {
        Hibernate5Module hibernate5Module = new Hibernate5Module();
        hibernate5Module.enable( Hibernate5Module.Feature.FORCE_LAZY_LOADING );
        hibernate5Module.disable( Hibernate5Module.Feature.USE_TRANSIENT_ANNOTATION );
        return hibernate5Module;
    }
}

これを使用すると、が得られcan't parse JSON. Raw result:ます。何か助けは?
Pra_A

1

このソリューションは、@ marcoによる以下のソリューションから発想を得たものです。私はまた、これらのデータで彼の答えを更新しました。

ここでの問題は、サブオブジェクトの遅延読み込みに関するもので、ジャクソンは完全に吹き飛ばされたオブジェクトではなく、休止状態のプロキシのみを見つけます。

したがって、2つのオプションが残されます-ここで最も投票された回答で上記のように例外を抑制するか、LazyLoadオブジェクトがロードされていることを確認します。

後者のオプションを選択した場合の解決策は、jackson-datatypeライブラリを使用し、シリアル化の前に遅延ロード依存関係を初期化するようにライブラリを構成することです。

そのために新しい構成クラスを追加しました。

@Configuration
public class JacksonConfig extends WebMvcConfigurerAdapter {

@Bean
@Primary
public MappingJackson2HttpMessageConverter jacksonMessageConverter(){
    MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
    ObjectMapper mapper = new ObjectMapper();
    Hibernate5Module module = new Hibernate5Module();
    module.enable(Hibernate5Module.Feature.FORCE_LAZY_LOADING);
    mapper.registerModule(module);
    messageConverter.setObjectMapper(mapper);
    return messageConverter;
}

}

@Primary他のBeanの初期化に他のJackson構成が使用されていないことを確認します。@Beanいつも通りです。module.enable(Hibernate5Module.Feature.FORCE_LAZY_LOADING);依存関係の遅延読み込みを有効にすることです。

注意-パフォーマンスへの影響に注意してください。EAGERフェッチが役立つこともありますが、それを熱心にしても、このコードが必要になります。ただし、プロキシオブジェクトは、@OneToOne

PS:一般的なコメントとして、Json応答でデータオブジェクト全体を送り返すことはお勧めしません。この通信にはDtoを使用し、mapstructなどのマッパーを使用してそれらをマップする必要があります。これにより、偶発的なセキュリティの抜け穴や上記の例外からあなたを守ります。


1

私は今、同じ問題を抱えています。@jsonIQgnoreで遅延フェッチを修正するかどうかを確認してください

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="teachers")
@JsonIgnoreProperties("course")
Teacher teach;

「(fetch = ...)」または注釈「@jsonIgnore」を削除するだけで機能します

@ManyToOne
@JoinColumn(name="teachers")
@JsonIgnoreProperties("course")
Teacher teach;

0

または、マッパーを次のように構成することもできます。

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

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

0

それは、問題を引き起こしているHibernateエンティティの関係である可能性があります...その関連エンティティの遅延読み込みを単に停止します...たとえば... customerTypeにlazy = "false"を設定することで以下を解決しました。

<class name="Customer" table="CUSTOMER">
        <id name="custId" type="long">
            <column name="CUSTID" />
            <generator class="assigned" />
        </id>
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        <property name="phone" type="java.lang.String">
            <column name="PHONE" />
        </property>
        <property name="pan" type="java.lang.String">
            <column name="PAN" />
        </property>

        <many-to-one name="customerType" not-null="true" lazy="false"></many-to-one>
    </class>
</hibernate-mapping>

1
実際のアプリケーションでは非常に危険な解決策になる可能性があることを投稿でお知らせください。
panurg

0

変更しました(注釈モデルクラス内)

fetch = FetchType.LAZY

fetch = FetchType.EAGER

そしてかなりの方法で働いた...

大好きです。


5
これは問題を解決しますが、非常に危険です。fetchモデルの戦略を変更すると、パフォーマンスにいくつかの影響があります。この変更により、データベースからより多くのデータを取り込むことができます。それは有効な解決策かもしれませんが、最初にパフォーマンス調査が必要です。
ジョアンMenighin

1
モデルであり、多くのエンティティが関連付けられていないことがわかっている場合は、わずかなメモリとパフォーマンスを使用して完全なエンティティを取得できます。LAZYフェッチを使用するのが常に最善であることを知っておくのは良いことです
シャムフィオリン2018

0

これはジャクソンの問題です。これを防ぐには、ネストされた関係またはネストされたクラスをシリアル化しないようにJacksonに指示します。

次の例を見てください。CityState、およびCountryクラスにマップされた住所クラス、およびState自体がCountryを指し、CountryがRegionを指している。SpringブートREST APIを介してアドレス値を取得すると、上記のエラーが発生します。これを防ぐには、マップされたクラス(レベル1のJSONを反映)をシリアル化し、との ネストされた関係を無視します。 @JsonIgnoreProperties(value = {"state"})@JsonIgnoreProperties(value = {"country"})@JsonIgnoreProperties(value = {"region"})

これにより、上記のエラーとともにLazyload例外が防止されます。以下のコードを例として使用して、モデルクラスを変更します。

Address.java

@Entity
public class Address extends AbstractAuditingEntity
{
    private static final long serialVersionUID = 4203344613880544060L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @Column(name = "street_name")
    private String streetName;

    @Column(name = "apartment")
    private String apartment;

    @ManyToOne
    @JoinColumn(name = "city_id")
    @JsonIgnoreProperties(value = {"state"})
    private City city;

    @ManyToOne
    @JoinColumn(name = "state_id")
    @JsonIgnoreProperties(value = {"country"})
    private State state;

    @ManyToOne
    @JoinColumn(name = "country_id")
    @JsonIgnoreProperties(value = {"region"})
    private Country country;

    @ManyToOne
    @JoinColumn(name = "region_id")
    private Region region;

    @Column(name = "zip_code")
    private String zipCode;

    @ManyToOne
    @JoinColumn(name = "address_type_id", referencedColumnName = "id")
    private AddressType addressType;

}

City.java

@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = "city")
@Cache(region = "cityCache",usage = CacheConcurrencyStrategy.READ_WRITE)
@Data
public class City extends AbstractAuditingEntity
{
    private static final long serialVersionUID = -8825045541258851493L;

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name")
    //@Length(max = 100,min = 2)
    private String name;


    @ManyToOne
    @JoinColumn(name = "state_id")
    private State state;
}

State.java

@Entity
@Table(name = "state")
@Data
@EqualsAndHashCode(callSuper = true)
public class State extends AbstractAuditingEntity
{
    private static final long serialVersionUID = 5553856435782266275L;

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "code")
    private String code;

    @Column(name = "name")
    @Length(max = 200, min = 2)
    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "country_id")
    private Country country;

}

Country.java

@Entity
@Table(name = "country")
@Data
@EqualsAndHashCode(callSuper = true)
public class Country extends AbstractAuditingEntity
{
    private static final long serialVersionUID = 6396100319470393108L;

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name")
    @Length(max = 200, min = 2)
    private String name;

    @Column(name = "code")
    @Length(max = 3, min = 2)
    private String code;

    @Column(name = "iso_code")
    @Length(max = 3, min = 2)
    private String isoCode;

    @ManyToOne
    @JoinColumn(name = "region_id")
    private Region region;
}

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.