본문 바로가기
Java & Kotlin/Spring Data

[JPA] illegally attempted to associate proxy with two open Sessions 에러

by heekng 2022. 7. 1.
반응형

illegally attempted to associate proxy with two open Sessions 에러

SpringBatch와 Jpa를 사용하면서 illegally attempted to associate proxy with two open Sessions에러를 만나게 됐다.

당연하지만 처음 보는 에러는 정말 당황스럽다.

illegally attempted to associate proxy with two open Sessions 에러는 두개 session에서 같은 proxy를 다루려 하면서 나타나는 에러다.

public MatchInfo process(MatchInfo matchInfo) throws Exception {
    initializeUnitsAndAugments(matchInfo);
    List<Augment> augments = getAugmentsByMatchInfo(matchInfo);
    List<Unit> units = getUnitsByMatchInfo(matchInfo);

    UseDeckAugment useDeckAugment = useDeckAugmentService.findOrSaveByAugmentsAndSeason(augments, matchInfo.getSeason());
    ...
}

위 코드를 보자, SpringBatch ItemReader를 통해 matchInfo를 받아와 findOrSaveByAugmentsAndSeason에서 matchInfo.getSeason() 값을 넘겨주었다.

@Transactional
public UseDeckAugment findOrSaveByAugmentsAndSeason(List<Augment> augments, Season season) {
    Optional<UseDeckAugment> useDeckAugmentOptional = useDeckAugmentRepository.searchByAugmentsAndSeason(augments, season);
    UseDeckAugment useDeckAugment;
    if (useDeckAugmentOptional.isPresent()) {
        useDeckAugment = useDeckAugmentOptional.get();
    } else {
        useDeckAugment = UseDeckAugment.builder()
                .season(season)
                .useCount(0L)
                .build();
        augments.stream()
                .map(augment ->
                    UseAugment.builder()
                            .augment(augment)
                            .build()
                )
                .forEach(useDeckAugment::insertUseAugment);
        useDeckAugmentRepository.save(useDeckAugment);
    }
    return useDeckAugment;
}

findOrSaveByAugmentsAndSeason는 넘겨받은 season과 augment를 이용해 엔티티를 생성한다.

처음 해당 에러를 만났을 때, Season season = seasonRepository.findById(season.getId()); 형태로 season을 새로 조회하여 해결하였다.

풀리지 않은 의문점은 똑같이 넘겨준 augments는 정상적으로 사용하는데 왜 season은 그렇지 않을까?였다.

2022-07-01 19:13:26.266  INFO 27878 --- [           main] c.h.a.service.UseDeckAugmentService      : season: com.heekng.api_toche_web.entity.Season@440d45c5

이유는 matchInfo의 season 객체가 proxy객체라는 것이였다.

public MatchInfo process(MatchInfo matchInfo) throws Exception {
    initializeUnitsAndAugments(matchInfo);
    List<Augment> augments = getAugmentsByMatchInfo(matchInfo);
    List<Unit> units = getUnitsByMatchInfo(matchInfo);

    UseDeckAugment useDeckAugment = useDeckAugmentService.findOrSaveByAugmentsAndSeason(augments, matchInfo.getSeason());
    ...
}

위의 initializeUnitsAndAugments는 units와 augments를 영속화하는 함수이다.

한번 영속화한 augments는 객체가 넘어가 사용되지만, matchInfo의 season은 영속화되지 않아 proxy 객체가 사용된 것이다.
또한 findOrSaveByAugmentsAndSeason 내에서 proxy 객체를 영속화하지 않았기 때문에 에러가 발생했다.

// initializeUnitsAndAugmentsAndSeason
...
Hibernate.initialize(matchInfo.getSeason());

위와 같이 Units와 Augments를 영속화하는 단게에서 season도 영속화하도록 수정하여 해결하였다.

반응형