programing

JSON을 쓸 수 없습니다. 역할 컬렉션을 게으르게 초기화하지 못했습니다.

lastmoon 2023. 7. 26. 22:19
반응형

JSON을 쓸 수 없습니다. 역할 컬렉션을 게으르게 초기화하지 못했습니다.

JSON을 반환하는 Java, Hibernate, Spring으로 REST 서비스를 구현하려고 했습니다.

저는 많은 관계를 가지고 있습니다.저는 재료 목록을 가지고 있는 공급자가 있고, 각 재료는 공급자 목록을 가지고 있습니다.

테이블을 만들었습니다.

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


    }
}

및 Id 추상 개체

public abstract class IdAbstractObject{

    public Long id;

}

문제는 엔드포인트를 호출할 때입니다.

http://localhost:8080/supplier/1

오류가 발생했습니다.

"JSON을 쓸 수 없습니다. 역할 모음인 myPackage를 게으르게 초기화하지 못했습니다.재료.구성 요소.공급자, 프록시를 초기화할 수 없습니다. 세션이 없습니다. 중첩 예외는 com.fasterxml.jackson.databind입니다.JsonMappingException: 역할 컬렉션 myPackage를 게으르게 초기화하지 못했습니다.재료.구성 요소.공급업체, 프록시를 초기화할 수 없습니다.

  • 세션이 없습니다(참조 체인: myPackage를 통해).공급자.공급업체 개체["성분"]->org.hibernate.collection.내부의.persistentBag[0]->myPackage.재료.성분["공급업체"])"

나는 이것을 따랐습니다.

가져오지 않은 게으른 개체에 대한 Jackson 직렬화 방지

이제 오류는 없지만 json에서 반환된 성분 필드는 null입니다.

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

하지만 디버그에서 나는 재료를 볼 수 있습니다...

enter image description here

이것은 Hibernate 및 Jackson Marshaller의 일반적인 동작입니다. 기본적으로 당신은 모든 공급업체 개체 세부 정보를 가진 JSON을 원합니다.재료를 포함했습니다.

이 경우 JSON 자체를 만들 때 주기적인 참조를 가질 수 있으므로 주의해야 합니다. 따라서 주석도 사용해야 합니다.

먼저 공급업체와 공급업체의 모든 세부 정보(성분 포함)를 로드해야 합니다.

어떻게 할 수 있죠?몇 가지 전략을 사용하여...를 사용합니다.DAO(또는 리포지토리) 구현(기본적으로 최대 절전 모드 세션을 사용하는 경우)에 있는 최대 절전 모드 세션을 닫기 전에 이 옵션을 사용해야 합니다.

따라서 이 경우(Hibernate를 사용하는 것으로 가정함) 저장소 클래스에서 다음과 같은 내용을 작성해야 합니다.

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

이제 당신이 가지고 있습니다.Supplier세부 정보를 (자체부세개정포는체하함모두보를▁with개(▁object체자는▁(▁all하▁its)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;
}

이런 식으로 잭슨은 JSON을 작성할 수 있습니다.but을 살펴봅시다.Ingredient 속성을 .다음과 같은 속성을 가집니다.

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

잭슨이 JSON을 만들려고 하면 어떻게 될까요?은 내의각요액다니합 안에 각 입니다.List<Ingredient>도 마찬가지이며 입니다.공업체 목록급참인조다니입적주기. 은 그것을 주석을 하면 피할 수 .따라서 이를 피해야 하며 JsonIgnore 주석을 사용하여 피할 수 있습니다.예를 들어, 당신은 당신의 것을 쓸 수 있습니다.Ingredient엔티티 클래스는 다음과 같습니다.

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

이러한 방식으로 다음을 수행할 수 있습니다.

  • 공급업체 개체에 모든 관련 성분을 적재합니다.
  • JSON 자체를 생성하려고 할 때 순환 참조를 피합니다.

어떤 경우에도 JSON을 마샬링 및 마샬링 해제하는 데 사용할 특정 DTO(또는 VO) 개체를 생성할 것을 제안합니다.

이것이 유용하기를 바랍니다.

안젤로

이 문제를 해결하기 위한 몇 가지 솔루션이 있습니다.

  1. 사용할 수 있습니다.@ManyToMany(fetch = FetchType.EAGER)

그러나 성능 측면에서 보면 EARGER 페치는 매우 좋지 않습니다.게다가, 일단 여러분이 열성적인 협회를 가지면, 여러분이 그것을 게으르게 만들 수 있는 방법은 없습니다.

  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

제 프로젝트에서 저는 당신의 프로젝트와 같은 문제를 발견했습니다.문제는 데이터를 "1대 다수"로 읽을 때까지 세션이 이미 닫혔다는 것입니다.모든 데이터를 가져오려면 트랜잭션을 명시적으로 초기화하거나 사용해야 합니다.명시적인 초기화를 사용했습니다.DAO에 줄을 추가해야 합니다.

Hibernate.initialize(supplier.getIngredients());

그런 다음 최대 절전 모드에서 데이터베이스의 모든 데이터를 로드합니다.할 때 을 추가합니다.@JsonIgnore1대 다 모델 필드의 주석입니다.

다음은 내 코드의 예입니다.

1.모델

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

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나고끝 @oneToMany당신의 모델 수업에서.

Jackson-data type-hibernate를 사용해야 합니다.

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

Application.java에 추가합니다.

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

이는 유휴 초기화가 시작되기 전에 최대 절전 모드 세션이 닫혔기 때문입니다.

솔루션은 아래의 이 답변에서 잘 설명되어 있습니다.가져오지 않은 게으른 개체에 대한 Jackson 직렬화 방지

내 application.properties에서 spring jpa의 open-in-view 속성이 false였습니다.저는 이걸 없애기 위해 댓글을 달아야 했습니다.

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

이것이 누군가에게 도움이 되길 바랍니다.

추가해야 했습니다.spring.jpa.open-in-view = trueapplication.properties에

언급URL : https://stackoverflow.com/questions/48117059/could-not-write-json-failed-to-lazily-initialize-a-collection-of-role

반응형