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

1u4esq0p  于 2022-11-08  发布在  其他
关注(0)|答案(8)|浏览(193)

我尝试用java -hib- spring实现一个服务器REST,它返回一个json。
我已经Map了多对多关系。
我解释得更好,我有一个供应商,有一个成分列表,每个成分都有一个供应商列表。
我创建了表:

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

    }
}

标识抽象对象

public abstract class IdAbstractObject{

    public Long id;

}

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

http://localhost:8080/supplier/1

出现错误:
“无法写入JSON:无法延迟初始化角色集合:无法初始化代理-无会话;嵌套的异常是com.fasterxml.Jackson.databind.jsonMappingException:无法延迟初始化角色集合:myPackage.ingredient.Ingredient.suppliers,无法初始化代理-无会话(通过引用链:我的软件包.供应商.供应商对象[\“成分"]-〉组织.休眠.集合.内部.持久性包[0]-〉我的软件包.成分.成分[\“供应商"])”
我接着说:
避免对未提取的惰性对象进行Jackson序列化
现在我还没有这个错误但是在json返回的时候,配料字段是null:

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

但是在调试中我可以看到一些成分....

vh0rcniy

vh0rcniy1#

这是Hibernate和JacksonMarshaller的正常行为。基本上,您需要具有以下内容:包含所有供应商对象详细信息的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
我希望这是有用的
安杰洛

a2mppw5e

a2mppw5e2#

您有一些解决此问题的解决方案:
1.您可以使用@ManyToMany(fetch = FetchType.EAGER)
但是从性能的Angular 来看,EAGER取数是非常糟糕的。而且,一旦你有了EAGER关联,你就不可能让它变得懒惰。
1.您可以使用@ManyToMany @Fetch(FetchMode.JOIN)
更多信息:https://docs.jboss.org/hibernate/orm/3.2/api/org/hibernate/FetchMode.html
编辑:当yout application.properties文件中包含以下行时,可能会发生此错误:

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

rqenqsqc3#

在我的项目中,我遇到了与您的项目相同的问题。问题是在“一对多”读取数据时,会话已经关闭。要获取所有数据,您需要显式初始化或使用事务。我使用了显式初始化。您需要在DAO中添加一行:

Hibernate.initialize(supplier.getIngredients());

之后,Hibernate将从数据库加载所有数据,为了避免序列化到JSON时产生异常,我在一对多模型字段中添加了@JsonIgnore注解。
下面是我的代码示例:
1.Model

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

2.数据访问对象

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

uklbhaso4#

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

zynd9foi

zynd9foi5#

这是由于休眠会话在惰性初始化开始之前关闭。
下面的答案很好地解释了解决方案。避免对非获取的懒惰对象进行Jackson序列化

d5vmydt9

d5vmydt96#

您应该使用jackson-datatype-hib。
https://github.com/FasterXML/jackson-datatype-hibernate
并将其添加到您的www.example.com上Application.java

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

jv4diomz7#

在我的application.properties文件中,spring jpa的open-in-view属性是假的。我必须通过注解来消除这个错误。


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

希望这能帮助到一些人。

f2uvfpb9

f2uvfpb98#

我不得不将spring.jpa.open-in-view = true添加到application.properties

相关问题