无法写入 JSON:无法延迟初始化角色集合

新手上路,请多包涵

我尝试用 java - hibernate - spring 实现服务器 REST,它返回一个 json。

我映射了多对多关系。

我解释得更好,我有一个有配料清单的供应商,每种配料都有一个供应商清单。

我创建了表:

 CREATE TABLE supplier_ingredient (
  supplier_id BIGINT,
  ingredient_id BIGINT
)

ALTER TABLE supplier_ingredient ADD CONSTRAINT supplier_ingredient_pkey
PRIMARY KEY(supplier_id, ingredient_id);

ALTER TABLE supplier_ingredient ADD CONSTRAINT
fk_supplier_ingredient_ingredient_id FOREIGN KEY (ingredient_id)
REFERENCES ingredient(id);

ALTER TABLE supplier_ingredient ADD CONSTRAINT
fk_supplier_ingredient_supplier_id FOREIGN KEY (supplier_id) REFERENCES
supplier(id);

然后我有 成分 模型:

 .....
.....
@ManyToMany(mappedBy = "ingredients")
@OrderBy("created DESC")
@BatchSize(size = 1000)
private List<Supplier> suppliers = new ArrayList<>();
....
....

然后我有 供应商 模型:

 ....
@ManyToMany
@JoinTable( name = "supplier_ingredient ",
        joinColumns = @JoinColumn(name = "supplier_id", referencedColumnName = "id"),
        inverseJoinColumns = @JoinColumn(name = "ingredient_id", referencedColumnName = "id"),
        foreignKey = @ForeignKey(name = "fk_supplier_ingredient_supplier_id"))
@OrderBy("created DESC")
@Cascade(CascadeType.SAVE_UPDATE)
@BatchSize(size = 1000)
private List<Ingredient> ingredients = new ArrayList<>();
....

端点

 @RequestMapping(value = "/{supplierId:[0-9]+}", method = RequestMethod.GET)
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
public SupplierObject get(@PathVariable Long supplierId) {

    Supplier supplier = supplierService.get(supplierId);

    SupplierObject supplierObject = new SupplierObject (supplier);

    return SupplierObject;

}

服务

....
public Supplier get(Long supplierId) {

    Supplier supplier = supplierDao.getById(supplierId); (it does entityManager.find(entityClass, id))

    if (supplier == null) throw new ResourceNotFound("supplier", supplierId);

    return supplier;
}
....

供应商对象

    @JsonIgnoreProperties(ignoreUnknown = true)
    public class SupplierObject extends IdAbstractObject {

    public String email;

    public String phoneNumber;

    public String address;

    public String responsible;

    public String companyName;

    public String vat;

    public List<Ingredient> ingredients = new ArrayList<>();

    public SupplierObject () {

    }

    public SupplierObject (Supplier supplier) {

        id = supplier.getId();
        email = supplier.getEmail();
        responsible = supplier.getResponsible();
        companyName = supplier.getCompanyName();
        phoneNumber = supplier.getPhone_number();
        ingredients = supplier.getIngredients();
        vat = supplier.getVat();
        address = supplier.getAddress();

    }
}

IdAbstractObject

 public abstract class IdAbstractObject{

    public Long id;

}

我的问题是,当我调用端点时:

 http://localhost:8080/supplier/1

我收到一个错误:

“无法写入 JSON:无法延迟初始化角色集合:myPackage.ingredient.Ingredient.suppliers,无法初始化代理 - 无会话;嵌套异常是 com.fasterxml.jackson.databind.JsonMappingException:无法延迟初始化集合角色:myPackage.ingredient.Ingredient.suppliers,无法初始化代理 - 无会话(通过引用链:myPackage.supplier.SupplierObject[\“ingredients\”]->org.hibernate.collection.internal.PersistentBag[0]- >myPackage.ingredient.Ingredient[\“供应商\”])”

我跟着这个:

避免对非获取的惰性对象进行 Jackson 序列化

现在我没有错误,但在返回的 json 中,成分字段为空:

 {
  "id": 1,
  "email": "mail@gmail.com",
  "phoneNumber": null,
  "address": null,
  "responsible": null,
  "companyName": "Company name",
  "vat": "vat number",
  "ingredients": null
}

但在调试中我可以看到成分….

在此处输入图像描述

原文由 Gjord83 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 429
2 个回答

这是 Hibernate 和 Jackson Marshaller 的正常行为 基本上您需要以下内容:一个包含所有 Supplier 对象详细信息的 JSON…包括成分。

请注意,在这种情况下,您必须非常小心,因为当您尝试创建 JSON 本身时可能会有循环引用,因此您还应该使用 JsonIgnore 注释

您必须做的第一件事是加载供应商及其所有详细信息(包括成分)。

你怎么能这样做?通过使用多种策略…让我们使用 Hibernate.initialize 。这 必须在 DAO(或存储库)实现(基本上是您使用休眠会话的地方)中的休眠会话关闭之前 使用。

所以在这种情况下(我假设使用 Hibernate)在我的存储库类中我应该写这样的东西:

 public Supplier findByKey(Long id)
{
    Supplier result = (Supplier) getSession().find(Supplier.class, id);
    Hibernate.initialize(result.getIngredients());
    return result;
}

现在你有了 Supplier 对象及其所有详细信息( Ingredients 也)现在在你的服务中你可以做你所做的是:

 @RequestMapping(value = "/{supplierId:[0-9]+}", method = RequestMethod.GET)
@ResponseStatus(value = HttpStatus.OK)
@ResponseBody
public SupplierObject get(@PathVariable Long supplierId)
{
    Supplier supplier = supplierService.get(supplierId);
    SupplierObject supplierObject = new SupplierObject (supplier);
    return SupplierObject;
}

通过这种方式,Jackson 能够编写 JSON but 让我们看看 Ingredient 对象。它具有以下属性:

 @ManyToMany(mappedBy = "ingredients")
@OrderBy("created DESC")
@BatchSize(size = 1000)
private List<Supplier> suppliers = new ArrayList<>();

当 Jackson 尝试创建 JSON 时会发生什么?它将访问 List<Ingredient> 中的每个元素,它也会尝试为这个元素创建一个 JSON……也为供应商列表创建一个 JSON,这是一个循环引用……所以你必须避免它,您可以使用 JsonIgnore 注释来避免它。例如,您可以这样编写 Ingredient 实体类:

 @JsonIgnoreProperties(value= {"suppliers"})
public class Ingredient implements Serializable
{
......
}

这样你:

  • 用所有相关成分加载供应商对象
  • 尝试创建 JSON 本身时避免循环引用

无论如何,我建议您创建特定的 DTO(或 VO)对象以用于编组和解组 JSON

我希望这是有用的

安吉洛

原文由 Angelo Immediata 发布,翻译遵循 CC BY-SA 3.0 许可协议

您有一些解决方案来解决此问题:

  1. 您可以使用 @ManyToMany(fetch = FetchType.EAGER)

但是从性能的角度来看,EAGER 抓取是非常糟糕的。此外,一旦你有了 EAGER 联想,你就无法让它变得 LAZY。

  1. 您可以使用 @ManyToMany @Fetch(FetchMode.JOIN)

更多信息: https ://docs.jboss.org/hibernate/orm/3.2/api/org/hibernate/FetchMode.html

编辑:当您在 application.properties 文件中有以下行时,可能会发生这种情况:

 spring.jpa.open-in-view = false

原文由 veben 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题