【问题标题】:No serializer found for class org.hibernate.proxy.pojo.javassist.Javassist?没有为类 org.hibernate.proxy.pojo.javassist.Javassist 找到序列化程序?
【发布时间】:2014-09-19 14:14:12
【问题描述】:

我正在处理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;
    }

    }

我该如何解决这个问题?

【问题讨论】:

  • 请显示堆栈跟踪和发生异常的代码
  • 在不知道代码试图做什么的情况下,调试起来有点困难,但您可能需要查看github.com/FasterXML/jackson-datatype-hibernate,因为您使用的是 Jackson 和 Hibernate
  • 你想从这个类中创建一个 JSON 吗?在这种情况下,JSON 序列化程序会尝试写入所有属性,以及多对多关系的 HashSet;这会产生一个惰性初始化程序异常
  • 同样的问题可以在stackoverflow.com/questions/4362104/…找到
  • @user2963481... 很好,很有帮助的问题,兄弟。

标签: json spring hibernate


【解决方案1】:

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

对我有用

【讨论】:

    【解决方案2】:

    有两种方法可以解决问题。

    方式 1

    添加 spring.jackson.serialization.fail-on-empty-beans=false 进入 application.properties

    方式二

    在 JPQL 查询中使用join fetch 来检索父对象数据,见下文:

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

    【讨论】:

    • 谢谢!第一个解决方案太棒了!
    • 方式 1。太好了。谢谢。
    【解决方案3】:

    我现在也有同样的问题。 检查您是否使用@jsonIQgnore 修复了延迟获取

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

    只需删除“(fetch=...)”或注释“@jsonIgnore”就可以了

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

    【讨论】:

      【解决方案4】:

      这是杰克逊的问题。为防止这种情况,请指示 Jackson 不要序列化嵌套关系或嵌套类。

      看下面的例子。 Address 类映射到 CityStateCountry 类,并且 State 本身指向 Country,Country 指向到地区。当您通过 Spring Boot REST API 获取地址值时,您将收到上述错误。为了防止它,只需序列化映射类(反映一级 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;
      }
      

      【讨论】:

        【解决方案5】:

        该解决方案的灵感来自@marco 的以下解决方案。我还用这些数据更新了他的答案。

        这里的问题是关于子对象的延迟加载,Jackson 只找到休眠代理,而不是完整的对象。

        所以我们有两个选择 - 抑制异常,就像上面在大多数投票的答案中所做的那样,或者确保加载 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 确保没有其他 Jackson 配置用于初始化任何其他 bean。 @Bean 和往常一样。 module.enable(Hibernate5Module.Feature.FORCE_LAZY_LOADING); 是开启延迟加载依赖。

        注意 - 请注意其性能影响。有时 EAGER fetch 会有所帮助,但即使你让它变得渴望,你仍然需要这段代码,因为除了 @OneToOne 之外的所有其他映射仍然存在代理对象

        PS:作为一般评论,我不鼓励在 Json 响应中将整个数据对象发送回的做法,应该使用 Dto 进行这种通信,并使用一些映射器(如 mapstruct)来映射它们。这样可以避免意外的安全漏洞以及上述异常。

        【讨论】:

          【解决方案6】:

          对我有用

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

          【讨论】:

            【解决方案7】:

            对于 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:。有什么帮助吗?
            【解决方案8】:

            我改变了(在 Annotation 模型类中)

            fetch = FetchType.LAZY

            fetch = FetchType.EAGER

            并以漂亮的方式工作......

            喜欢它。

            【讨论】:

            • 虽然这确实解决了问题,但它非常危险。更改模型上的fetch 策略会对性能产生多种影响。通过此更改,您将从数据库中获取更多数据。这可能是一个有效的解决方案,但首先需要进行性能研究。
            • 如果你知道你是模型,并且不会有很多相关的实体,你可以花一点内存和性能来获得一个完整的实体。但很高兴知道最好使用 LAZY fetch
            【解决方案9】:

            只是为了添加这个,我遇到了同样的问题,但提供的答案不起作用。我通过接受异常的建议并添加到 application.properties 文件来修复它...

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

            我正在使用带有 Hibernate 4.3 的 Spring Boot v1.3

            它现在序列化整个对象和嵌套对象。

            编辑:2018 年

            因为这仍然会得到 cmets,所以我会在这里澄清一下。这绝对只会隐藏错误。性能影响就在那里。当时,我需要一些东西来交付并在以后处理它(我通过不再使用弹簧来做到这一点)。所以,是的,如果你真的想解决这个问题,请听听别人的意见。如果您现在只想让它消失,请继续使用此答案。这是一个糟糕的主意,但见鬼,可能对你有用。为了记录,在此之后再也没有发生过崩溃或问题。但这可能是最终成为 SQL 性能噩梦的根源。

            【讨论】:

            • 解决了这个问题但是json会包含两个额外的不需要的属性"handler":{},"hibernateLazyInitializer":{}
            • 太棒了!也固定在这里,这就是答案
            • @prettyvoid 为什么存在额外的属性?
            • @RobertMoon 如果你想摆脱它们,你可以用@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})注释你的实体
            • @prettyvoid 谢谢。我发现另一个解决方案是将 LAZY 更改为 EAGER。对性能有影响吗?
            【解决方案10】:

            将此注释添加到适用于我的实体类(模型) 这会导致通过休眠代理对象延迟加载。

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

            【讨论】:

              【解决方案11】:

              在 Hibernate 5.2 及更高版本中,您可以删除 hibernate 代理,如下所示,它将为您提供实际对象,以便您可以正确序列化它:

              Object unproxiedEntity = Hibernate.unproxy( proxy );
              

              【讨论】:

              • 它也会预先自动调用Hibernate.initialize
              • @Tim,应该在json序列化之前加上。出现这个问题是因为在序列化过程中发现了休眠代理对象,所以一旦删除它,序列化就可以了。如果你使用的是弹簧控制器,那么你必须在控制器结束之前完成。
              【解决方案12】:

              我在通过休眠代理对象延迟加载时遇到了类似的问题。通过使用以下方式注释具有延迟加载的私有属性的类来解决它:

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

              我假设您可以在代理对象上添加打破 JSON 序列化到该注释的属性。

              问题是实体被延迟加载,并且序列化发生在它们完全加载之前。

              Hibernate.initialize(<your getter method>);
              

              【讨论】:

              • 它也对我有用...这种奇怪的行为有什么解释吗?
              • @ankur-singhal 您应该强调嵌套类而不是调用类需要此注释。
              • Yahooo... 它奏效了。我尝试了几次并且工作。我尝试了几种方法和 BoooM。工作。
              • 谢谢也为我工作。您需要将此注释添加到访问具有延迟加载 bean 的其他实体的实体。
              • 我认为这不是解决问题的正确方法。当我们序列化时,我们期望在结果中返回完整的子对象,或者至少子对象的主键。添加这些注释只会抑制错误,但不会为您提供所需的结果。
              【解决方案13】:

              这个异常

              org.springframework.http.converter.HttpMessageNotWritableException

              因为,我希望如此,您将响应输出作为可序列化对象发送。
              这是春天出现的问题。为了解决这个问题,发送 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.
              

              【讨论】:

              • 是的,我也在做同样的事情,但即使做同样的事情,我也会遇到同样的错误,因为我发送的对象关联数据只不过是设置实体类
              【解决方案14】:

              这可能是您的 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>
              

              【讨论】:

              • 请在您的帖子中告知它在现实生活中的应用中可能是非常危险的解决方案。
              【解决方案15】:

              或者您可以将映射器配置为:

              // 延迟加载的自定义配置

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

              【讨论】:

                【解决方案16】:

                正如之前的答案中正确建议的那样,延迟加载意味着当您从数据库中获取对象时,不会获取嵌套对象(并且可能会在以后需要时获取)。

                现在 Jackson 尝试序列化嵌套对象(== 从中生成 JSON),但失败了,因为它找到了 JavassistLazyInitializer 而不是普通对象。这是您看到的错误。那么,如何解决呢?

                正如 CP510 之前所建议的,一种选择是通过这行配置来抑制错误:

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

                但这是处理症状,而不是原因。要优雅地解决它,您需要决定是否需要 JSON 中的这个对象?

                1. 如果您需要 JSON 格式的对象,请从导致它的字段中删除 FetchType.LAZY 选项(它也可能是某些嵌套对象中的字段,不仅在您正在获取的根实体中)。

                2. 如果不需要JSON中的对象,则使用@JsonIgnore注释该字段(或字段本身,如果您也不需要接受传入的值)的getter,例如:

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

                如果您有更复杂的需求(例如,使用同一实体的不同 REST 控制器的不同规则),您可以使用 jackson viewsfiltering 或者对于非常简单的用例,分别获取嵌套对象。

                【讨论】:

                • 这是最好的答案。解决问题的根源,不要只是掩盖下一个程序员必须处理的症状。
                • 你是我的英雄。
                • 非常感谢您解释错误背后的原因并提供两个有意义的解决方案
                • 谢谢你,但是我看到 springfox-swagger 仍然给我错误RangeError: Maximum call stack size exceeded
                【解决方案17】:

                我认为问题在于您检索实体的方式。

                也许你正在做这样的事情:

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

                尝试使用get的方法,而不是load

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

                问题在于,使用 load 方法,您得到的只是一个代理,而不是真实的对象。代理对象没有已经加载的属性,所以当序列化发生时,没有要序列化的属性。使用 get 方法你实际上得到了真正的对象,这个对象实际上可以被序列化。

                【讨论】:

                • 当我使用 getOne 而不是 findOne 时,发生了一个非常相似的错误。就我而言,我的接口存储库正在扩展 JpaRepository。 findOne 运行良好,无需任何 Json 注释或 application.properties 中的更改
                • 像 James Freitas 一样,我也发现 getOne 导致了问题,并且使用例如 findById -解决了问题,而无需在应用程序属性中使用注释或上面给出的行
                • 我的工作伙伴澄清了我并告诉我:“基本的区别是 getOne 是延迟加载的,而 findOne 不是” - 这显然意味着前者实际上并没有从数据库中获取数据行,而只是创建参考。后者实际上是获取行,这是经常需要的情况
                • @JamesFreitas 是的!谢谢!感谢 Carlos 带领 James 得出这个美好的结论!
                【解决方案18】:

                您可以使用处理 Hibernate 延迟加载的 Jackson 附加模块。

                更多关于 https://github.com/FasterXML/jackson-datatype-hibernate 的信息,分别支持休眠 3 和 4。

                【讨论】:

                • 这应该是正确的答案。所有其他答案都在隐藏问题而不是解决问题。
                • 但是如何使用这个模块呢?有什么指南吗?
                • jackson-datatype-hibernate的配置方式:stackoverflow.com/q/33727017
                【解决方案19】:

                试试

                implements interface Serializable

                【讨论】:

                  猜你喜欢
                  • 2019-03-10
                  • 1970-01-01
                  • 1970-01-01
                  • 2019-12-14
                  • 2014-02-17
                  • 1970-01-01
                  • 2017-05-27
                  • 1970-01-01
                  • 2014-11-21
                  相关资源
                  最近更新 更多