我正在使用两个用@OneToMany (parent) @ManyToOne (child) 注释的 JPA 实体,我还编写了一个 RessourceAssembler 来将实体转换为 Springboot 应用程序 Controller 中的资源(请参见下面的代码示例).
如果父实体中没有@OneToMany 关系,资源组装和序列化工作得很好。
一旦我在父级上添加 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\"])"
如您所见,无限循环来自 hateoas 资源,而不是实体本身。
我已经尝试在实体上添加@JsonManagedReference 和@JsonBackReference 或在子对象上添加@JsonIgnore,但没有任何帮助。一旦嵌入子实体,Hateoas RessourceAssembler 总是以无限循环结束。似乎 shose @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
}
我正在为父实体“Storage”使用如下所示的 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"));
}
}
在 Controller 中,我有 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 中的这个无限循环。我想要的是嵌入式子条目要么不链接回父条目(因此不会创建到父条目的链接),要么它们包含一个级别的链接但不进行进一步处理。
最佳答案
当模型属性定义了延迟加载时,为了处理与使用 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 {...
或者您可以单方面声明映射注释 Storage 实体声明并更改 private Storage 存储;
以获取 EAGER @ManyToOne(fetch = FetchType.EAGER)
in Stock类。
@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
}
关于spring-boot - 将实体与 OneToMany 和 HATEOAS RessourceAssembler 一起使用会导致无限递归,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58367732/
相关文章:
javascript - 如何使用 yarn 将 NodeJS/React 应用程序部署到 Hero
ruby-on-rails - 如何使用 Vue 前端和 Rails 后端实现 env 变量?是否需
haskell - Cabal/Stack 忽略自定义安装脚本的 ghc-options
cmake - 安装问题: Cmake error : include could not find
oauth-2.0 - 松弛 Oauth : Automatically authorize use
azure - 服务主体帐户本地 AD Connect 同步