Spring Boot微服务InvalidDefinitionException问题求助
我开发了一个Spring Boot微服务,调用接口http://localhost:8080/user/utenti/时触发如下错误:
InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.ArrayList[0]->com.application.dto.UserDto["applicationRoleID"]->com.poste.anagrafica.entity.Role$HibernateProxy$N0iZZA2I["hibernateLazyInitializer"])
数据库结构

Entity类(User)
@Entity @Table(name = "USERS") @Data @NoArgsConstructor @AllArgsConstructor @Getter @Setter public class User implements Serializable { @Id @Column(name = "user_id", nullable = false) @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "created_date") private LocalDateTime createdDate; @Column(name = "deleted", nullable = false) private Long number; @Column(name = "modified_date") private LocalDateTime modifiedDate; @Column(name = "birth_place") private String birthPlace; @Column(name = "birthday") private Date birthDay; @Column(name = "canNominate") private Long canNominate; @Column(name = "email") private String email; @Column(name = "firstName") private String firstName; @Column(name= "fiscalCode") private String fiscalCode; @Column(name = "hiringDate") private Date hiringDate; @Column(name ="last_name") private String lastName; @Column(name = "matricola") private String matricola; @Column(name = "position") private String position; @Column(name = "registration_number") private String registrationNumber; @Column(name = "replaced") private Long replaced; @Column(name = "terminationDate") private Date terminationDate; @Column(name = "user_status") private String userStatus; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "application_role_id") private Role applicationRoleID; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "company_id") private Company companyID; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "direction_id") private Direction directions ; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "level_id") private Levels levelID; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "referent_id") private User referentID; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "role_id") private Role roleID; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "task_id") private EmployeeTask taskID; }
UserDTO类
@Data public class UserDto { private Long userId; private LocalDateTime createdDate; private Long number; private LocalDateTime modifiedDate; private String birthPlace; private Date birthDay; private Long canNominate; private String email; private String firstName; private String fiscalCode; private Date hiringDate; private String lastName; private String matricola; private String position; private String registrationNumber; private Long replaced; private Date terminationDate; private String userStatus; private Role applicationRoleID; private Company companyID; private Direction directions ; private Levels levelID; private User referentID; private Role role; private EmployeeTask taskID; }
Controller类
@RestController @RequestMapping("/user") @Log public class UserController { private static final Logger logger = LoggerFactory.getLogger(UserController.class); @Autowired UserService userService; @GetMapping(value = "/cerca/{registration_number}", produces = "application/json") public ResponseEntity<List<UserDto>> listUserByRegistrationNumber(@PathVariable("registration_number") String registrationNumber) throws ChangeSetPersister.NotFoundException { log.info("****** Ottengo l'user con numeroRegistrazione " + registrationNumber + " *******"); List<UserDto> user = userService.SelByRegistrationNumber(registrationNumber); return new ResponseEntity<List<UserDto>>(user, HttpStatus.OK); } @GetMapping(value = "/utenti", produces = "application/json") public ResponseEntity<List<UserDto>> listAllUsers ()throws ChangeSetPersister.NotFoundException{ List<UserDto> user = userService.SelTutti(); return new ResponseEntity<List<UserDto>>(user, HttpStatus.OK); } }
application.properties配置
spring.datasource.url = jdbc:oracle:thin:@192.134.2.82:1521/orcl spring.datasource.username =admin spring.datasource.password =zzzx spring.datasource.driver-class-name=oracle.jdbc.OracleDriver # JPA settings spring.jpa.database-platform=org.hibernate.dialect.Oracle12cDialect spring.jpa.hibernate.use-new-id-generator-mappings=false spring.jpa.hibernate.ddl-auto=update spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
这个错误核心是Hibernate懒加载代理对象无法被Jackson序列化:使用FetchType.LAZY关联实体时,Hibernate会生成ByteBuddy代理类替代真实实体,Jackson序列化这些代理时会访问内部的hibernateLazyInitializer属性,而该属性无对应序列化器,导致报错。
以下是几种可行解决办法,按推荐优先级排序:
1. DTO中避免直接引用Entity类(最推荐)
当前UserDto直接引用Role、Company等Entity类是问题根源。正确做法是为关联实体创建对应DTO,仅暴露需要返回的字段:
比如创建RoleDto:
@Data public class RoleDto { private Long id; private String roleName; // 其他需要返回的字段 }
修改UserDto中的关联字段:
// 替换原有的Role applicationRoleID; private RoleDto applicationRoleID; // 同理将Company、Direction等字段替换为对应DTO
在Service层转换时,手动复制Entity属性到DTO,或用MapStruct工具简化转换,彻底避免序列化代理对象的问题。
2. 禁用Jackson的FAIL_ON_EMPTY_BEANS特性
在Spring Boot配置中添加以下内容,让Jackson忽略空对象的序列化错误:
spring.jackson.serialization.fail-on-empty-beans=false
或通过配置类手动配置:
@Configuration public class JacksonConfig { @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); return mapper; } }
此方法简单但属于治标不治本,可能隐藏其他序列化问题,不推荐长期使用。
3. 强制加载懒加载关联对象
在Service层查询User时,使用JOIN FETCH强制加载需要的关联实体,避免生成代理对象:
// 在UserRepository的查询方法中使用JOIN FETCH @Query("SELECT u FROM User u JOIN FETCH u.applicationRoleID JOIN FETCH u.companyID ...") List<User> findAllWithAssociations();
适合关联实体较少的场景,但关联层级复杂时会导致查询语句臃肿,还可能引发N+1查询问题。
4. 使用Hibernate的@JsonIgnoreProperties注解
在Entity类的懒加载字段上添加注解,让Jackson忽略代理对象的内部属性:
@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "application_role_id") @JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) private Role applicationRoleID;
无需修改DTO,但如果关联实体本身有空字段,仍可能触发序列化问题,且会暴露整个Entity的字段,不符合DTO设计原则。
内容的提问来源于stack exchange,提问作者Michele Raso

