• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    미해결

컬렉션의 초기화 유무의 따른 size의 값이 다르게 측정됩니다.

23.11.01 03:20 작성 23.11.01 03:30 수정 조회수 214

1

    @Transactional
    public void increasePoint() {
        Store store1 = storeRepository.findById(1L).get();

        Point point = new Point(1);
//        System.out.println("store1.getPoints().size() = " + store1.getPoints().size());
        store1.add(point);
        store1.add(point);
        store1.add(point);
        store1.add(point);
        store1.add(point);
        System.out.println("store1.getPoints().size() = " + store1.getPoints().size());
    }
@OneToMany(mappedBy = "store",cascade = CascadeType.ALL)
private List<Point> points = new ArrayList<>();

Store(1):Point(N) 입니다. Store 내부에 양방향으로 List로 갖고있고 현재 디비에 Store ID 1번을 외래키로 갖고있는 Point가 이미 2개가 있습니다.

 

위에서 첫번째 디버그 출력문을 주석처리하고 add를 5번 진행 후 마지막에 검증하게 되면 DB에 2개 + 1개 해서 사이즈가 3으로 측정됩니다.

하지만 위에서 주석처리를 제거하고 size()를 통해 초기화 이후에 add를 5번 진행하고 사이즈를 출력하면 DB2개 + 5개해서 7개가 출력됩니다.

 

  1. 위에 두가지 상황에서 왜 초기화를 먼저 진행하고 add하는것과 add이후에 초기화 하는것이 결과가 다른지 궁금합니다.

     

  2. 같은 객체를 여러번 add해도 실제 DB에 인서트 쿼리는 1번만 발생하게 됩니다(위에 두 가지 경우 모두) 짐작은 되지만 정확한 이해가 되지 않는데.. 같은 객체라 영속성컨텍스트에서 관리하게 되면 이후에것은 이미 관리하고 있으니 저장하지 않는것으로 이해하면 될까요?

  3. 위에서 point객체가 영속성컨텍스트에 관리되기 시작하는 시점은 언제인가요? 최초의 add호출인가요?


    PersistentBag의 add를 봤을때 컬렉션이 초기화 되어있지않다면 다른 리스트에 넣어두고 초기화되면서 합쳐주는것으로 확인했는데 왜 5개를 다 넣어주지 않고 1개만 넣어주는지 궁금합니다. (디버깅과정에서 자동초기화 되서 정확하지 않습니다.)

     

     

답변 2

·

답변을 작성해보세요.

0

안녕하세요. YOGURT님

여기에는 2가지 문제가 있습니다.

  1. 같은 인스턴스를 컬렉션에 저장하고 있습니다. 다음과 같이 다른 인스턴스를 컬렉션에 저장하도록 변경해주세요.

        store1.add(new Point(1));
        store1.add(new Point(1));
        store1.add(new Point(1));
        store1.add(new Point(1));
        store1.add(new Point(1));
  1. EqualsHashCode가 잘못 설정되어 있습니다.

@EqualsAndHashCode
public class Point {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private Long id;

    private int point;
}

이렇게 하면 모든 필드를 사용해서 Equals와 HashCode를 만들게 됩니다.

그런데!

id는 IDENTITY 전략이기 때문에 데이터베이스에 보관하기 전까지는 항상 null이 됩니다. 추가로 point도 모두 1로 같습니다. 따라서 equals, hashcode를 사용하는 컬렉션 입장에서는 인스턴스가 달라도 같다고 생각하는 문제가 발생합니다.

이 문제를 해결하는 가장 단순한 방법은 @EqualsAndHashCode 롬복을 제거하는 것입니다. 그러면 인스턴스마다 모두 다른 equals, hashcode를 가지기 때문에 이런 문제가 발생하지 않습니다.

감사합니다.

0

안녕하세요. YOGURT님

정확한 상황을 파악하기 위해서

실제 동작하는 전체 프로젝트를 압축해서 구글 드라이브로 공유해서 링크를 남겨주세요.

구글 드라이브 업로드 방법은 다음을 참고해주세요.

https://bit.ly/3fX6ygx


주의: 업로드시 링크에 있는 권한 문제 꼭 확인해주세요

 

추가로 다음 내용도 코멘트 부탁드립니다.

1. 문제 영역을 실행할 수 있는 방법

2. 문제가 어떻게 나타나는지에 대한 상세한 설명

감사합니다.

YOGURT님의 프로필

YOGURT

질문자

2023.11.01

https://drive.google.com/file/d/1OyxS-CHF97lLdYZ32cfKr9VOoDtL3iTT/view?usp=sharing

1. 문제 영역을 실행할 수 있는 방법

docker-compose 실행 후 디비 연결 한 뒤 데이터를 먼저 넣어둡니다. store(1),point(2)
그 상태로 서버를 실행 시키고 localhost:8080/api 를 호출하면 increasePoint() 메서드가 동작하는데 이때 store.points를 size()로 초기화 하고 add할때와 하지 않고 add할때 결과값이 다릅니다(add하고 나서 size를 호출하는 곳에서)

이렇게 결과가 다른 이유가 궁금합니다!