Jackson序列化Hibernate懒加载代理类报错问题排查
已检查所有实体模型,所有属性均具备getter和setter方法。通过添加配置spring.jackson.serialization.fail-on-empty-beans=false可临时规避报错,但这仅为隐藏异常的权宜之计,想了解该问题的根本原因是什么?
异常详情
org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.ArrayList[0]->com.auzmor.platform.models.results.notes.NoteResult["mentions"]->java.util.ArrayList[0]->com.auzmor.platform.models.entities.Employee_$$_jvst80f_13["handler"])
错误请求信息
ServletWebRequest: uri=/api/v1/notes;client=0:0:0:0:0:0:0:1;user=com.auzmor.platform.configurations.auth.CustomPrincipal@3d7c4c43
错误信息:Type definition error: [simple type, class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.ArrayList[0]->com.auzmor.platform.models.results.notes.NoteResult["mentions"]->java.util.ArrayList[0]->com.auzmor.platform.models.entities.Employee_$$jvst80f_13["handler"])
错误原因:com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.ArrayList[0]->com.auzmor.platform.models.results.notes.NoteResult["mentions"]->java.util.ArrayList[0]->com.auzmor.platform.models.entities.Employee$$_jvst80f_13["handler"])
相关实体类代码
CandidateComment
@Data @Getter @Setter @AllArgsConstructor @NoArgsConstructor @Table(name = "candidate_comments") @DynamicInsert @javax.persistence.Entity @DynamicUpdate @ToString public class CandidateComment extends Entity implements Serializable { @ApiModelProperty(notes="Text of the note") @Size(min = MIN_SIZE, message="text should have at least 1 characters") @Size(max = MAX_TEXT_SIZE, message="text should not exceed 2048 characters") private String text; private boolean isExternalAgency; @JsonBackReference @OneToMany(mappedBy = "comment", fetch = FetchType.EAGER ,cascade = CascadeType.ALL) @JsonIgnoreProperties("comment") protected List<CommentMention> mentions; @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL) @JoinColumn(name = "author_id", insertable = false, updatable = false) @JsonIgnoreProperties({"fullName","mobile"}) protected Employee author; @JsonIgnore @Column(name = "author_id") protected Long authorPK; @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "candidate_id", insertable = false, updatable = false) protected Candidate candidate; @JsonIgnore @Column(name = "candidate_id") protected Long candidatePK; }
CommentMention
@Getter @Setter @AllArgsConstructor @Table(name = "comment_mentions") @DynamicInsert @javax.persistence.Entity @DynamicUpdate @NoArgsConstructor public class CommentMention extends BaseEntity implements Serializable { @JsonBackReference @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL) @JoinColumn(name = "comment_id", insertable = false, updatable = false) @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) protected CandidateComment comment; @JsonIgnore @Column(name = "comment_id") protected Long commentPK; @JsonIgnore @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "user_id", insertable = false, updatable = false) @JsonIgnoreProperties({"fullName","mobile"}) protected Employee user; @JsonIgnore @Column(name = "user_id") protected Long userPK; }
从异常的引用链可以明确问题根源:
CommentMention中的user字段采用FetchType.LAZY懒加载策略,Hibernate会为Employee实体生成代理类(如Employee_$$_jvst80f_13),该代理类包含handler属性(类型为JavassistLazyInitializer),用于控制懒加载的初始化逻辑。- 当Spring MVC序列化
NoteResult为JSON时,Jackson遍历到mentions列表中的CommentMention对象,进而访问其user属性。此时user的代理对象未被初始化(未触发数据库查询加载真实数据),Jackson会尝试序列化代理对象的所有属性,包括handler。 JavassistLazyInitializer类没有Jackson可识别的可序列化属性(无公开字段或可被检测到的getter方法),而Jackson默认的FAIL_ON_EMPTY_BEANS特性会对这类对象抛出序列化异常。
你添加的临时配置只是关闭了异常检测,跳过空对象序列化,但并未解决代理对象被错误序列化的核心问题。
1. 配置Jackson Hibernate模块(推荐)
添加Jackson专门处理Hibernate懒加载的模块(如jackson-datatype-hibernate5),它能自动识别Hibernate代理对象,只序列化真实实体数据,忽略代理相关属性:
- Maven依赖:
<dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-hibernate5</artifactId> </dependency>
- Spring配置注册模块:
@Configuration public class JacksonConfig { @Bean public Module hibernateModule() { return new Hibernate5Module(); } }
2. 修正实体的@JsonIgnoreProperties配置
在CommentMention的user字段上,添加忽略代理相关属性的配置:
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler", "fullName","mobile"}) protected Employee user;
让Jackson序列化时跳过handler和hibernateLazyInitializer属性,避免触发异常。
3. 确保懒加载关联在序列化前初始化
在业务逻辑层提前触发懒加载关联的初始化:
// 调用任意get方法触发懒加载 commentMention.getUser().getId(); // 或使用Hibernate工具类初始化 Hibernate.initialize(commentMention.getUser());
也可以在JPQL查询中使用FETCH JOIN,一次性加载关联的Employee数据:
SELECT cm FROM CommentMention cm JOIN FETCH cm.user WHERE ...
4. 移除临时配置
不要长期使用spring.jackson.serialization.fail-on-empty-beans=false,它会掩盖其他真实的空对象序列化问题,导致业务异常无法及时发现。
内容的提问来源于stack exchange,提问作者RYX

