我正在使用两个标有@OneToMany(父级)的JPA实体<-> @ManyToOne(子级),并且我还编写了一个RessourceAssembler来将这些实体转换为Springboot应用程序控制器中的资源(请参见下面的代码示例) )。
在父实体中没有@OneToMany关系时,Ressource组装和序列化就可以了。
在父级上添加OneToMany关系后,序列化就会与此中断:
"Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: org.springframework.hateoas.Resource[\"content\"]->com.marcelser.app.entities.Storage[\"storageStock\"])"
如您所见,无限循环来自仇恨资源,而不是实体本身。
我已经尝试在实体上添加@JsonManagedReference和@JsonBackReference或在子级上添加@JsonIgnore,但没有任何真正的帮助。嵌入子实体后,Hateeoas RessourceAssembler总是以无限循环结束。似乎缺少@Json ....批注有助于实体本身的JSON序列化,但它们不能解决RessourceAssembler的问题
我有这两个实体(存储和库存)
@Entity
@Table(name = "storage")
@Data
public class Storage {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
@OneToMany(cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
mappedBy = "storage")
private Set<Stock> storageStock = new HashSet<>();;
}
@Entity
@Table(name = "stock")
@Data
public class Stock {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
@JsonIgnore
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "storage_id")
private Storage storage;
... other fields ommitted
}
并且我正在对父实体“存储”使用如下所示的RessourceAssemlber:
@Component
public class StorageResourceAssembler implements ResourceAssembler<Storage, Resource<Storage>> {
@Override
public Resource<Storage> toResource(Storage storage) {
return new Resource<>(storage,
linkTo(methodOn(StorageController.class).one(storage.getId())).withSelfRel(),
linkTo(methodOn(StorageController.class).all()).withRel("storages"));
}
}
并且在控制器中,我有2个获取类来列出全部或仅一个带有子项的存储
public class StorageController {
private final StorageRepository repository;
private final StorageResourceAssembler assembler;
@GetMapping
ResponseEntity<?> all() {
List<Resource<Storage>> storages = repository.findAll().stream()
.map(assembler::toResource)
.collect(Collectors.toList());
Resources<Resource<Storage>> resources = new Resources<>(storages,
linkTo(methodOn(StorageController.class).all()).withSelfRel());
return ResponseEntity.ok(resources);
}
private static final Logger log = LoggerFactory.getLogger(StorageController.class);
StorageController(StorageRepository repository, StorageResourceAssembler assembler) {
this.repository = repository;
this.assembler = assembler;
}
@GetMapping("/{id}")
ResponseEntity<?> one(@PathVariable Long id) {
try {
Storage storage = repository.findById(id)
.orElseThrow(() -> new EntityNotFoundException(id));
Resource<Storage> resource = assembler.toResource(storage);
return ResponseEntity.ok(resource);
}
catch (EntityNotFoundException e) {
log.info(e.getLocalizedMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body (new VndErrors.VndError("Storage not found", "could not find storage with id " + id ));
}
}
... omitted Put/Post/Delete
}
任何人都可以启发我如何解决HateOAS中的无限循环。我想要的是嵌入的子项只是不链接回父项(因此不会创建到父项的链接),或者它们包含一个级别的链接,但是没有进一步的处理。
答案 0 :(得分:0)
要在模型属性定义为延迟加载时处理与使用Jackson API进行模型序列化有关的问题,我们必须告诉序列化器忽略Hibernate添加到类的链或有用的垃圾,以便它可以管理通过声明@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
注释来延迟加载数据。
@Entity
@Table(name = "storage")
@Data
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Storage {...
@Entity
@Table(name = "stock")
@Data
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Stock {...
或者您可以只声明声明存储实体声明的单边映射,并更改private Storage storage;
以获取Stock类中的EAGER @ManyToOne(fetch = FetchType.EAGER)
。
@Entity
@Table(name = "storage")
@Data
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Storage {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
/*@OneToMany(cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
mappedBy = "storage")
private Set<Stock> storageStock = new HashSet<>();;*/
}
@Entity
@Table(name = "stock")
@Data
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Stock {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
@JsonIgnore
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "storage_id")
private Storage storage;
... other fields ommitted
}
答案 1 :(得分:-1)
也许有点晚,但是我遇到了这个问题或非常相似,我只找到了一个解决方案。相同的错误500为我提供了解决方法的线索:
Type definition error: [simple type, class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: org.springframework.hateoas.PagedModel["_embedded"]->java.util.Collections$UnmodifiableMap["usuarios"]->java.util.ArrayList[0]->org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module$PersistentEntityResourceSerializer$1["content"]->com.tfg.modelos.Usuario["rol"]->org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module$PersistentEntityResourceSerializer$1["content"]->com.tfg.modelos.Rol$HibernateProxy$QFcQnzTB["hibernateLazyInitializer"])
所以我只需要添加application.properties:
spring.jackson.serialization.FAIL_ON_EMPTY_BEANS=false