본문 바로가기
Spring 이해하기

[JPA] Collection 을 업데이트 할때, add()와 addAll() 을 조심하자.

by simplify-len 2021. 6. 5.

JPA 에서 Collection 을 업데이트 할 때,  다음과 같은 에러를 받았다.

o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 23505, SQLState: 23505
o.h.engine.jdbc.spi.SqlExceptionHelper   : Unique index or primary key violation: "PUBLIC.UK_9NEY9DAVBULF79NMN9VG6K7TN_INDEX_2 ON PUBLIC.LINE(NAME) VALUES 2"; SQL statement:
update line set created_date=?, modified_date=?, color=?, name=? where id=? [23505-200]

 

받았던 이유는 line entity 의 name 이 unique 하기 때문이다.

@Entity
public class Line extends BaseEntity {
	...
    
    @Column(unique = true)
    private String name;
	
	...
}

 

이제 update 하는 service 코드를 살펴보면, 다음과 같다.

    public LineResponse updateById(final Long lineId, final LineRequest lineRequest) {
        Line origin = lineRepository.findById(lineId)
                .orElseThrow(() -> new DataIntegrityViolationException("Not Found lineId" + lineId));

        Line line1 = lineRequest.toLine();
        Station downStation = stationRepository.findById(lineRequest.getDownStationId()).orElseThrow(() -> new DataIntegrityViolationException("Not Fount downStationId" + lineRequest.getDownStationId()));
        Station upStation = stationRepository.findById(lineRequest.getUpStationId()).orElseThrow(() -> new DataIntegrityViolationException("Not Fount downStationId" + lineRequest.getUpStationId()));
        int distance = lineRequest.getDistance();

        Section section = new Section(upStation, downStation, distance);
        line1.addSection(section);
        origin.update(line1);

        origin.getSections().getValues().clear();

        List<Section> values = origin.getSections().getValues();

        /* addAll() 에서 에러가 발생했던 이유는 line1.getSections()에서 insert 가 발생한다. */
        List<Section> values2 = line1.getSections().getValues();
        values.addAll(values2); // line1 의 정보를 가져야 한다. 그래서 JPA 에서는 origin line 과 같이 insert 를 하려고 한다.

        List<Station> stations = origin.getStations();
        return LineResponse.of(origin, stations);
    }

코드를 잘 살펴보면, 에러가 나는 이유를 쉽게 파악하기 힘들었다. line1 이라는 아직 ID를 부여받지 않은 Line Entity 의 부분적인 값을 업데이트 해줄 것이라 예상했지만, 실제로 JPA 의 매직은 line1에 대한 Persist 를 자동으로 만든다.

Hibernate: 
    insert 
    into
        line
        (id, created_date, modified_date, color, name) 
    values
        (null, ?, ?, ?, ?)
Hibernate: 
    insert 
    into
        section
        (id, created_date, modified_date, distance, down_station_id, line_id, up_station_id) 
    values
        (null, ?, ?, ?, ?, ?, ?)
Hibernate: 
    update
        line 
    set
        created_date=?,
        modified_date=?,
        color=?,
        name=? 
    where
        id=?

line 에 값이 들어있다.

 

왜 업데이트를 하는데, Insert 구문이 왜 나가는지, 쉽사리 알아차리기 힘들었는데 JPA에서 자동으로 Entity 를 Persist 하는 바람에 발생했다.

 

addAll()  로 업데이트 하는 것이 아니라 아래와 같이 add() 하면 문제가 발생하지 않는다.

List<Section> values = origin.getSections().getValues();
values.add(new Section(downStation,upStation, distance));

line 이 Null 이다.

댓글