无法写入 JSON:无法延迟初始化角色集合
Posted
技术标签:
【中文标题】无法写入 JSON:无法延迟初始化角色集合【英文标题】:Could not write JSON: failed to lazily initialize a collection of role 【发布时间】:2018-06-15 11:16:49 【问题描述】:我尝试使用 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);
然后我有Ingredient模型:
.....
.....
@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;
....
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();
和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,无法初始化代理 - 无 Session(通过引用链:myPackage.supplier.SupplierObject[\"ingredients\"]->org.hibernate.collection.internal.PersistentBag[0]->myPackage.ingredient.Ingredient[\"suppliers\"])"
我跟着这个:
Avoid Jackson serialization on non fetched lazy objects
现在我没有错误,但是在返回的 json 中,成分字段为空:
"id": 1,
"email": "mail@gmail.com",
"phoneNumber": null,
"address": null,
"responsible": null,
"companyName": "Company name",
"vat": "vat number",
"ingredients": null
但在调试中我可以看到成分......
【问题讨论】:
【参考方案1】:这是 Hibernate 和 Jackson Marshaller 的正常行为 基本上,您希望拥有以下内容:包含所有供应商对象详细信息的 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 ......同样适用于供应商列表,这是一个循环引用......所以你必须避免它,你可以通过使用 JsonIgnore 注释来避免它。例如你可以这样写你的Ingredient
实体类:
@JsonIgnoreProperties(value= "suppliers")
public class Ingredient implements Serializable
......
这样你:
使用所有相关成分加载供应商对象 尝试创建 JSON 本身时避免循环引用在任何情况下,我都会建议您创建特定的 DTO(或 VO)对象以用于编组和解组 JSON
希望对你有用
安杰洛
【讨论】:
【参考方案2】:您有一些解决方案可以解决此问题:
-
您可以使用
@ManyToMany(fetch = FetchType.LAZY)
但是从性能的角度来看,EAGER fetching 非常糟糕。而且,一旦你有了一个 EAGER 关联,你就无法让它变得 LAZY。
-
您可以使用
@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
【讨论】:
改变:spring.jpa.open-in-view = true 对我有用...【参考方案3】:在我的项目中,我遇到了与您相同的问题。问题是,在“一对多”读取数据时,会话已经关闭。要获取所有数据,您需要显式初始化或使用事务。我使用了显式初始化。 你需要在DAO中添加一行:
Hibernate.initialize(supplier.getIngredients());
之后,Hibernate 将从数据库中加载所有数据。为了避免在序列化为 JSON 时产生异常,我在一对多模型字段中添加了 @JsonIgnore
注释。
这是我的代码示例:
1.型号
@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();
【讨论】:
我的关系是多对多,反正我不明白你的建议。首先你说你需要在DAO中添加一行: Hibernate.initialize(supplier.getIngredients());然后你写一个 DAO ......我很困惑【参考方案4】:只需在模型类中的@oneToMany
之后添加@JsonIgnore
。
【讨论】:
【参考方案5】:这是由于休眠会话在延迟初始化启动之前关闭。
解决方案在下面的这个答案中得到了很好的解释。 Avoid Jackson serialization on non fetched lazy objects
【讨论】:
所以,我尝试了您链接的示例,但现在返回的 json 中的成分为空: "id": 1, "email": "123@gmail.com", "phoneNumber ": null, "address": null, "responsible": null, "companyName": "123123", "vat": "ddstere", "ingredients": null 我正在使用 Hibernate 5,为此,我正在使用Hibernate5Module 而不是 Hibernate4Module,就像在您的链接中一样,我确信,我在调试中看到,成分存在【参考方案6】:您应该使用 jackson-datatype-hibernate。
https://github.com/FasterXML/jackson-datatype-hibernate
并将其添加到您的 Application.java
@Bean
public ObjectMapper objectMapper()
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new Hibernate5Module());
return objectMapper;
【讨论】:
【参考方案7】:在我的 application.properties 文件中,spring jpa 的 open-in-view 属性是错误的。我必须注释掉才能摆脱这个。
#spring.jpa.open-in-view=false
希望这对某人有所帮助。
【讨论】:
以上是关于无法写入 JSON:无法延迟初始化角色集合的主要内容,如果未能解决你的问题,请参考以下文章
休眠延迟加载不适用于 Spring Boot => 无法延迟初始化角色集合无法初始化代理 - 无会话
org.hibernate.LazyInitializationException:无法延迟初始化角色集合:FQPropretyName,无法初始化代理 - 无会话
LazyInitializationException: 延迟初始化角色集合失败 无法初始化代理 - 没有会话
无法延迟初始化角色集合:myapp.myapp.models.Contact.messages,无法初始化代理 - 没有会话
未能延迟初始化角色集合:无法初始化代理 - Hibernate 中的 @ManyToMany 映射注释中没有会话错误? [复制]