서론
개인 프로젝트를 진행하면서 복합키를 사용할 일이 생겨 진행을 하는 도중 발생된 문제점과 해결 과정을 정리한 내용입니다
테이블의 구성은 user와 group이 다대다로 사이에 중간 매핑 테이블을 두어 각 PK를 사용하여 복합키를 설정하였다
문제점
@Entity
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class UserGroup implements Persistable<UserGroupId> {
@EmbeddedId
private UserGroupId id;
@Enumerated(EnumType.STRING)
private GroupRole groupRole;
@MapsId("userId")
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "USER_ID")
private User user;
@MapsId("groupId")
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "GROUP_ID")
private Group group;
public static UserGroup newMember(UserGroupId id, User user, Group group) {
return new UserGroup(id, ROLE_UN_APPROVED, user, group);
}
}
엔티티는 위와 같이 설정이 되어있고 로직은 다음과 같다
public void saveUserGroupWithId(String userId, Long groupId) {
User user = userRepository.findById(userId);
Group group = groupRepository.findById(groupId);
userGroupRepository.save(newMember(createUserGroupId(userId, groupId), user, group));
}
여기서 문제점은 단순히 저장만 하는 것이 아니라 조회 쿼리까지 총 3회의 쿼리가 실행되었다는 점이다
만일 연관관계를 맺을 테이블이 늘어나게 된다면 조회 로직이 추가적으로 실행될 수 있다는 문제가 있다
이를 해결하기 위해 프록시 객체를 조회하여 사용하기로 했고 조회 쿼리을 제거할 수 있게 되었다
해결 코드
public void saveUserGroupWithId(String userId, Long groupId) {
User user = userRepository.getReferenceById(userId);
Group group = groupRepository.getReferenceById(groupId);
userGroupRepository.save(newMember(createUserGroupId(userId, groupId), user, group));
}
그럼 여기서 프록시는 무엇이고 JPA에서 프록시 객체를 조회하면 왜 조회 쿼리가 나가지 않는지를 알아보자
프록시란
proxy를 번역기에 돌려보면 대리, 대리권이라는 뜻으로 해석된다
즉 JPA에서는 진짜 Entity를 대신하는 가짜 Entity라고 생각하면 될 듯하다
proxy pattern에 대해서 설명하면 proxy 객체가 진짜 객체를 참조하여 가지고 있는 상태이다
JPA 프록시 객체 특징
- 처음 사용할 때(객체에 대한 정보를 가져올 때:
getName()
,getId()
등 호출 시) 한 번만 초기화 된다 - 프록시 객체 초기화 시 진짜 객체로 바뀌는 것이 아니라 계속 프록시 객체를 통한 참조가 이루어진다
- 프록시 객체 초기화 시 실제 객체가 생성되어 있지 않다면 DB조회 후 영속성 컨텍스트에 저장해둔다
조회 쿼리가 나가지 않는 이유
객체에 대한 정보를 요청하지 않았으므로 영속성 컨텍스트에 실제 객체를 초기화 해둘 필요없고 그로 인해 DB로부터 조회쿼리를 보낼 필요가 없다
정리
지금과 같은 상황처럼 단순 저장만 할 경우 연관관계 설정을 위해 객체를 조회하고 설정한 후 저장을 하는 것은 불필요한 쿼리를 날리게 된다
그렇다해서 연관관계를 설정하지 않고 null 값으로 두고 저장을 하게 된다면 IdentifierGenerationException
이 발생하게 된다
추가적인 조회 쿼리를 날릴 바에는 proxy 객체를 사용하여 추후 사용 시에 쿼리를 날리는 것이 좋아보인다.
참고
'공부기록 > JPA' 카테고리의 다른 글
JPA - 복합키 (0) | 2023.03.19 |
---|---|
JPA- JPQL (0) | 2022.01.31 |
JPA-JPQL (0) | 2022.01.28 |
JPA-값타입 (0) | 2022.01.27 |
JPA-프록시 (0) | 2022.01.26 |