简体   繁体   English

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

[英]Could not write JSON: failed to lazily initialize a collection of role

I tried to implement a REST service with Java, Hibernate, and Spring, which returns JSON.我尝试使用 Java、Hibernate 和 Spring 实现 REST 服务,返回 JSON。

I have map a many to many relation.我有 map 多对多关系。 I have a supplier that has a list of ingredients, and each ingredient has a list of suppliers.我有一个有成分列表的供应商,每个成分都有一个供应商列表。

I created the table:我创建了表:

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);

Then I have an Ingredient model:然后我有一个成分model:

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

Then I have a Supplier model:然后我有一个供应商model:

....
@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<>();
....

Endpoint :端点

@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;

}

Service服务

....
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;
}
....

SupplierObject供应商对象

    @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();


    }
}

And IdAbstractObjectIdAbstractObject

public abstract class IdAbstractObject{

    public Long id;

}

My problem is, when I call the endpoint:我的问题是,当我调用端点时:

http://localhost:8080/supplier/1

I got an error:我收到一个错误:

"Could not write JSON: failed to lazily initialize a collection of role: myPackage.ingredient.Ingredient.suppliers, could not initialize proxy - no Session; nested exception is com.fasterxml.jackson.databind.JsonMappingException: failed to lazily initialize a collection of role: myPackage.ingredient.Ingredient.suppliers, could not initialize proxy “无法写入 JSON:无法延迟初始化角色集合:myPackage.ingredient.Ingredient.suppliers,无法初始化代理 - 否 Session;嵌套异常是 com.fasterxml.jackson.databind.JsonMappingException:无法延迟初始化集合角色:myPackage.ingredient.Ingredient.suppliers,无法初始化代理

  • no Session (through reference chain: myPackage.supplier.SupplierObject["ingredients"]->org.hibernate.collection.internal.PersistentBag[0]->myPackage.ingredient.Ingredient["suppliers"])"没有 Session(通过参考链:myPackage.supplier.SupplierObject["ingredients"]->org.hibernate.collection.internal.PersistentBag[0]->myPackage.ingredient.Ingredient["suppliers"])"

I followed this:我跟着这个:

Avoid Jackson serialization on non fetched lazy objects 避免对非获取的惰性对象进行 Jackson 序列化

Now I haven't the error but in json returned, the ingredients field is null:现在我没有错误,但在返回 json 时,成分字段为 null:

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

but in debug I can see ingredients....但在调试中我可以看到成分....

在此处输入图像描述

This is the normal behaviour of Hibernate and Jackson Marshaller Basically you want to have the following: a JSON with all Supplier object details... included the Ingredients.这是 Hibernate 和 Jackson Marshaller 的正常行为 基本上,您希望拥有以下内容:包含所有供应商对象详细信息的 JSON……包括成分。

Please note that in this case you must be very carefull because you can have a cyclic reference when you try to create the JSON itself so you should use also JsonIgnore annotation请注意,在这种情况下,您必须非常小心,因为当您尝试创建 JSON 本身时可能会有循环引用,因此您还应该使用JsonIgnore注释

The first thing you must do is to load the Supplier and all its details (ingredients included).您必须做的第一件事是加载供应商及其所有详细信息(包括成分)。

How can you do it?你怎么能做到呢? By using several strategies... let's use the Hibernate.initialize .通过使用几种策略......让我们使用Hibernate.initialize This must be used before the closing of hibernate session that is in the DAO (or repository) implementation (basically where you use the hibernate session).必须在关闭DAO(或存储库)实现中的休眠会话之前使用(基本上是您使用休眠会话的地方)。

So in this case (I assume to use Hibernate) in my repository class I should write something like this:因此,在这种情况下(我假设使用 Hibernate)在我的存储库类中,我应该编写如下内容:

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

Now you have the Supplier object with all its own details ( Ingredients too) Now in your service you can do what you did that is:现在您拥有了包含所有详细信息的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;
}

In this way Jackson is able in writing the JSON but let's give a look to the Ingredient object.. it has the following property:通过这种方式,Jackson 能够编写 JSON, but让我们看看Ingredient对象。它具有以下属性:

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

What will happen when Jackson tries to create the JSON?当 Jackson 尝试创建 JSON 时会发生什么? It will access to the each element inside the List<Ingredient> and it will try to create a JSON for this one too.... also for the suppliers list and this is a cyclic reference... so you must avoid it and you can avoid it by using the JsonIgnore annotation.它将访问List<Ingredient>的每个元素,它也会尝试为这个元素创建一个 JSON ......也用于供应商列表,这是一个循环引用......所以你必须避免它,你可以通过使用 JsonIgnore 注释来避免它。 For example you may write your Ingredient entity class in this way:例如,您可以以这种方式编写Ingredient实体类:

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

In this way you:通过这种方式,您:

  • load the supplier object with all the related ingredient使用所有相关成分加载供应商对象
  • avoid a cyclic reference when you try to create the JSON itself尝试创建 JSON 本身时避免循环引用

In any case I would suggest to you to create specific DTO (or VO) object to use for marshalling and unmarshalling JSONs在任何情况下,我都会建议您创建特定的 DTO(或 VO)对象以用于编组和解组 JSON

I hope this is usefull我希望这是有用的

Angelo安杰洛

You have some solutions to resolve this issue:您有一些解决方案可以解决此问题:

  1. You can use @ManyToMany(fetch = FetchType.LAZY)您可以使用@ManyToMany(fetch = FetchType.LAZY)

But EAGER fetching is very bad from a performance perspective.但是从性能的角度来看,EAGER 获取非常糟糕。 Moreover, once you have an EAGER association, there is no way you can make it LAZY.此外,一旦你有了一个 EAGER 协会,你就无法让它变得懒惰。

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

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

Edit: It can occur when you have the following line in yout application.properties file:编辑:当您的application.properties文件中有以下行时,可能会发生这种情况:

spring.jpa.open-in-view = false

In my project I came across the same problem as yours.在我的项目中,我遇到了和你一样的问题。 The problem is that by the time of reading the data "one to many" the session has already been closed.问题是,在“一对多”读取数据时,会话已经关闭。 To get all the data, you need to explicitly initialize or use the transaction.要获取所有数据,您需要显式初始化或使用事务。 I used an explicit initialization.我使用了显式初始化。 You need to add a line in the DAO:您需要在 DAO 中添加一行:

Hibernate.initialize(supplier.getIngredients());

After that, Hibernate will load all the data from the database.之后,Hibernate 将从数据库中加载所有数据。 To avoid generating an exception when serializing to JSON, I add the @JsonIgnore annotation in the one-to-many model field.为了避免在序列化为 JSON 时产生异常,我在一对多模型字段中添加了@JsonIgnore注解。

Here is an example of my code:这是我的代码示例:

1.Model 1.型号

@OneToMany(mappedBy = "commandByEv", fetch = FetchType.LAZY)
@JsonIgnore
private Set<Evaluation> evaluations;

2. DAO 2.DAO

public Command getCommand(long id) {
Session session = sessionFactory.getCurrentSession();
Evaluation evaluation = session.get(Evaluation.class, id);
Hibernate.initialize(evaluation.getCommand());
return evaluation.getCommand();
}

只需在模型类中的@JsonIgnore之后添加@JsonIgnore @oneToMany

This is due to the hibernate session closed before the lazy initialization kicked in.这是由于在延迟初始化开始之前休眠会话已关闭。

The solution is explained well at this answer below.解决方案在下面的这个答案中得到了很好的解释。 Avoid Jackson serialization on non fetched lazy objects 避免对未获取的惰性对象进行 Jackson 序列化

You should use a jackson-datatype-hibernate.您应该使用 jackson-datatype-hibernate。

https://github.com/FasterXML/jackson-datatype-hibernate https://github.com/FasterXML/jackson-datatype-hibernate

And add this on your Application.java并将其添加到您的 Application.java

@Bean
public ObjectMapper objectMapper() {
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.registerModule(new Hibernate5Module());
    return objectMapper;
}

In my application.properties file open-in-view property of spring jpa was false.在我的 application.properties 文件中,spring jpa 的 open-in-view 属性是错误的。 I had to comment out to get rid of this.我不得不发表评论以摆脱这一点。

 #spring.jpa.open-in-view=false

Hope this help somebody.希望这对某人有所帮助。

I had to add spring.jpa.open-in-view = true to application.properties我必须将spring.jpa.open-in-view = true添加到 application.properties

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 无法写入 JSON:未能一对一延迟初始化角色集合 - Could not write JSON: failed to lazily initialize a collection of role one to one 无法写入 JSON:未能延迟初始化角色集合: - Could not write JSON: failed to lazily initialize a collection of role: 无法编写JSON:无法延迟初始化集合 - Could not write JSON: failed to lazily initialize a collection 延迟获取具有多个关系的实体:无法写入 JSON:未能延迟初始化角色集合 - Lazily fetching entity with multiple relations: Could not write JSON: failed to lazily initialize a collection of role 无法编写内容:无法使用OpenEntityManagerInViewFilter延迟初始化角色集合 - Could not write content: failed to lazily initialize a collection of role using OpenEntityManagerInViewFilter 使用 springboot 和 hibernate 搜索“无法编写 JSON:无法延迟初始化角色集合” - "Could not write JSON: failed to lazily initialize a collection of role" with springboot and hibernate search 无法编写JSON:无法延迟初始化角色集合:com.managem.model.Region.pays,无法初始化代理-没有会话 - Could not write JSON: failed to lazily initialize a collection of role: com.managem.model.Region.pays, could not initialize proxy - no Session 休眠-无法延迟初始化角色集合:无法初始化代理-没有会话 - Hibernate - failed to lazily initialize a collection of role: could not initialize proxy - no Session 无法延迟初始化角色集合无法初始化代理-无会话 - Failed to lazily initialize a collection of role could not initialize proxy - no Session 无法延迟初始化角色集合:无法初始化代理-无会话 - failed to lazily initialize a collection of role : could not initialize proxy - no Session
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM