• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    미해결

영속성 컨텍스트와 Unique 제약 조건에 대해 질문 있습니다.

23.09.23 19:57 작성 조회수 248

0

영한님의 JPA 서적/강의로 JPA를 학습하는 동시에 개인 프로젝트를 병행하여 JPA에 대한 연습을 하고 있습니다.

Unique 제약 조건을 추가하는 과정에서 궁금증이 생겨 질문 드립니다.

 

  1. 영속성 컨텍스트는 그 자체로 Unique 제약 조건을 검증하지 않나요?

    1. chatGPT에게 질문을 했을 때의 '영속성 컨텍스트는 unique 제약 조건을 확인하지 않고 DB에 해당 역할을 위임한다; 라고 대답하였는데, 달리 검증할 방법을 모르겠어 이곳에 질문 드립니다.

  2. 아래와 같은 상황을 방지하기 위해서는 데이터 저장 로직에 em.flush()를 함께 써 주는 것이 가장 바람직한 접근 방법일까요?

    1. 영한님의 다른 답변을 참고하면 데이터 저장의 빈도수가 상대적으로 적기 때문에 em.flush()를 한번 더 호출하는 것이 큰 성능 저하를 가져오지 않을 것 같고, 문제 해결도 되는 것 같습니다만, 제가 놓치는 부분이 있을까 하여 질문 드립니다.

  3. 테스트에 @Rollback(value=false)를 추가하면 데이터가 예상한 대로 실패하던데, 해당 어노테이션의 유무가 어떤 차이를 가지기에 결과가 달라지는건지 함께 여쭤봐도 될까요??

강의 내용에 대한 질문보다는, 강의 내용에 기반을 둔 프로젝트 질문이라 여기에 질문을 드려도 되는지 모르겠습니다만, 혹여나 도움을 받을 수 있을까 하여 질문 드립니다. '영속성 컨텍스트의 동작 원리' 쯤 내용들은 관련 자료가 쉽게 찾아지지 않아 궁금증을 어떤 식으로 해결할 수 있을지 모르겠습니다 ㅠㅠ

질문 이해를 돕고자 아래에 문제 상황도 함께 추가하여 질문 드립니다.

 

배경 설명

질문 상황이 이해가 쉽도록 최대한 간단히 프로젝트 구성 설명 드립니다.

유저의 데이터를 저장하는 DAO를 구현 중입니다.

 

User 모델

@Entity
@Table(name = "USERS")
@Getter @Setter
@NoArgsConstructor
public class User {

    @Id
    @GeneratedValue
    @Column(name = "IDX")
    private Long idx;

    @Column(name = "NAME")
    private String userName;

    @Column(name = "ID", unique = true)
    private String id;

    @Column(name = "PASSWORD")
    private String password;
}

유저의 ID가 중복되지 않도록 ID 컬럼에만 @Column( ... unique = true) 를 사용하였습니다.

 

UserDAO

@Repository
public class UserDAO {

    @PersistenceContext
    private EntityManager em;

    public long save(User user){
        em.persist(user);
        return user.getIdx();
    }

    //...
}

마지막으로, Unique 제약 조건을 확인하기 위한 테스트 코드 입니다.

중복되는 ID 값을 가진 유저를 저장할 때 어떤 예외가 발생하는지 직접 확인하기 위해 실패하는 테스트 코드를 작성하였습니다.

 

UserDAOTest


@ExtendWith(SpringExtension.class)
@SpringBootTest
class DBUserDAOTest {

    @Autowired
    UserDAO userDAO;

    @Test
    @Transactional
    public void sameIdSaveTest(){
        //given
        DBUser user1 = new DBUser("User1", "sameId", "PW1");
        DBUser user2 = new DBUser("User2", "sameId", "PW2");

        //when
        long savedIdx1 = userDAO.save(user1);
        long savedIdx2 = userDAO.save(user2);

        //that
    }

 

문제 상황

동일한 ID 값인 "sameId"를 사용했음에도 문제가 없이 테스트가 통과하는 모습입니다.

 

로직 변경

public long save(DBUser user){
    em.persist(user);
    em.flush();
    return user.getIdx();
}

위와 같이 em.flush()를 추가하면

 

예상과 같이 테스트가 실패합니다.

DataIntegrityViolationException이 발생합니다.

 

답변 1

답변을 작성해보세요.

0

인프런 AI 인턴님의 프로필

인프런 AI 인턴

2023.09.25

안녕하세요, 인프런 AI 인턴입니다. 영속성 컨텍스트는 일반적으로 Unique 제약 조건을 검증하지 않습니다. 영속성 컨텍스트는 엔티티의 생명주기 관리와 관련된 작업을 수행하는데 초점이 맞추어져 있고, 데이터베이스 관련 작업은 JPA 구현체가 담당합니다.

따라서, Unique 제약 조건을 준수하려면 데이터 저장 로직에 추가로 검증을 수행해야 합니다. 데이터 저장 직전에 영속성 컨텍스트의 내용을 데이터베이스에 반영하기 위해 em.flush()를 사용하는 것은 일반적으로 옳은 방법입니다. em.flush()는 영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화하는 역할을 수행합니다. 따라서, Unique 제약 조건을 위반하는 데이터를 감지하고 예외를 발생시킬 수 있습니다.

@Rollback 어노테이션은 테스트의 롤백 여부를 지정하기 위해 사용되는 어노테이션입니다. @Rollback(true)를 설정하면 데이터베이스 작업이 롤백되어 테스트 이후에 저장된 데이터가 삭제됩니다. 반면, @Rollback(false)를 설정하면 테스트에 의해 저장된 데이터가 영구적으로 유지됩니다. 이 어노테이션을 사용해 테스트 결과에 따라 테스트 데이터를 유지하거나 삭제할 수 있습니다.

그러나 이와 관련하여 결과가 다르게 나타나는 이유는 단순히 @Rollback 어노테이션의 유무 때문이 아닙니다. 테스트 시에는 영속성 컨텍스트가 테스트 메서드마다 초기화되기 때문에 이전에 저장했던 엔티티 인스턴스는 영속성 컨텍스트에 존재하지 않기 때문에 Unique 제약 조건에 위배되는 데이터가 저장되는 것을 확인할 수 있습니다.

더 자세한 내용은 영한님의 JPA 서적/강의를 통해 학습하시는 것이 좋습니다. 감사합니다.