본문 바로가기

JPA

(14)
[JPA] deleteAll() 수행 후 바로 insert 했을 때 duplicate entry 에러가 발생하는 문제 프로젝트를 진행하면서 아래와 같은 로직을 작성했었습니다. 백준 이메일을 수정했을때 데이터베이스에 저장된 수정되기 전 백준 이메일로 푼 문제 리스트를 전부 삭제한 후 수정할 이메일로 푼 문제리스트를 다시 저장하는 로직입니다. @Override @Transactional public GithubBaekjoonResponseDto updateGithubAndBaekjoon(Long userId, GithubBaekjoonRequestDto requestDto) { User user = getUser(userId); user.updateEmail(requestDto.getGithub(), requestDto.getBaekjoon()); solvedacRepository.deleteAllByUserId(user...
[JPA] 벌크성 수정 쿼리 이번에 팀 프로젝트를 진행하면서 findAll() 메서드를 사용하여 리스트를 조회한 뒤 JPA 영속성 컨텍스트의 변경 감지기능을 사용해서 특정 조건의 데이터를 모두 업데이트 해주는 서비스 메서드를 구현했었습니다. 구현이 끝나고 테스트해보니 기능은 잘 동작하였지만 업데이트 되는 데이터 수만큼 쿼리가 나가고 있었습니다. 이런 상황을 해결하기 위해 JPA는 벌크성 수정을 효율적으로 수행할 수 있도록 지원해주고 있습니다. MemberRepository JPA에서 벌크성 업데이트를 수행하기 위해서는 Repository 클래스에서 @Query 어노테이션을 사용해서 update 쿼리를 작성하여 사용합니다. MemberRepository 클래스에 아래와 같은 메서드를 작성한 후 동일한 테스트를 실행하면 이번에는 업데..
[JPA] 고급 매핑 (상속 관계 매핑, @MappedSuperclass) 상속 관계 매핑 위 사진과 같이 id, 이름(name), 가격(price)이라는 동일한 컬럼을 사용하는 엔티티가 여러 개있는 경우 상속 구조를 이용해 공통되는 컬럼을 추출하여 구현할 수 있다. 구현하는 방법으로는 총 3가지가 있으며 부모 클래스까지 직접 테이블과 매핑되는 3가지 방법 외에 마지막으로 부모 클래스는 테이블과 매핑하지 않고 부모 클래스를 상속받는 자식 클래스에게 매핑 정보만 제공하는 @MappedSuperclass 까지 알아보자. 조인 전략 (Joined Strategy) 조인 전략은 엔티티 각각을 모두 테이블로 만들고 자식 테이블이 부모 테이블의 기본 키를 받아서 기본 키 + 외래 키로 사용하는 전략이다. 조회할 때 조인을 자주 사용하며 조인전략을 사용하기 위해서는 타입을 구분하는 컬럼을 ..
[Querydsl] 동적쿼리 작성하기 BooleanBuilder BooleanBuilder 클래스를 이용하여 파라미터가 null이 아닌 경우에만 and() 메소드로 조건을 추가하고 where 절에 builder 객체를 기입하여 사용한다. List result = searchMember1(usernameParam, ageParam); ... private List searchMember1(String usernameCond, Integer ageCond) { BooleanBuilder builder = new BooleanBuilder(); if (usernameCond != null) { builder.and(member.username.eq(usernameCond)); } if (ageCond != null) { builder.and(me..
[Querydsl] 프로젝션 프로젝션이란? select 절에 어떤 것을 가져올지 결정하는 것을 프로젝션이라고 한다. - 프로젝션 대상이 하나인 경우 타입을 명확하게 정할 수 있다. // String 값인 member.username 하나만 조회해 올때 List result = queryFactory .select(member.username) .from(member) .fetch(); - 프로젝션 대상이 둘 이상인 경우 튜플이나 DTO로 조회해온다. List result = queryFactory .select(member.username, member.age) .from(member) .fetch(); 이때 Tuple은 Querydsl에서 제공하는 Tuple이기 때문에 repository 계층에서 사용하는 것은 괜찮지만 servi..
[Querydsl] Querydsl 적용하기 Querydsl 사용이유 SpringBoot와 Spring Data JPA의 조합으로 개발자가 직접 SQL 문을 작성할 필요없이 메소드로 동일한 기능을 제공해주기 때문에 매우 편리해졌지만 아직도 복잡한 쿼리와 동적 쿼리를 작성하는데는 한계가 있다. Querydsl은 위와 같은 문제점을 해결해주고 여러가지 편리한 기능들을 제공해준다. 아래 코드는 member1이라는 username을 갖는 Member를 조회하는 기능을 JPQL과 Querydsl을 이용하여 테스트한 코드이다. 먼저 JPQL의 경우 sql문을 String 타입으로 작성했지만 Querydsl은 자바코드로 작성됐다. 그렇기 때문에 JPQL은 해당 기능을 사용하기 전에는 무엇이 잘못되었는지 확인할 수 없지만(런타임 에러) Querydsl은 컴파일 ..
[JPA] N + 1 문제와 해결방법 N + 1 문제란? 처음 조회한 데이터 수만큼 다시 SQL을 사용해서 연관 관계가 설정된 엔티티를 조회하는 것을 N + 1 문제라고 한다. N + 1 문제는 em.find() 메소드나 스프링 데이터 JPA의 findById() 메소드 같은 단건 조회 시에는 연관된 엔티티를 조인해서 한번에 조회해오기 때문에 문제가 되지 않지만 JPQL을 이용한 메소드를 호출할 때는 문제가 발생한다. N + 1 상황을 만들기 위해 아래와 같이 엔티티를 설정했다. Member 엔티티 @Entity @Getter @Setter @NoArgsConstructor public class Member { @Id @GeneratedValue @Column(name = "MEMBER_ID") private Long id; private..
[JPA] 객체지향 쿼리 객체지향 쿼리란? 객체지향 쿼리란 데이터베이스 테이블을 대상으로 조회하는 SQL과 달리 엔티티 객체를 대상으로 조회하는 쿼리를 의미한다. 가장 중요한 객체지향 언어로 JPQL(Java Persistence Query language)이 있다. JPQL이 가장 중요한 이유는 이 글에서 추가로 소개할 Criteria, QueryDSL 같은 기술들은 JPQL을 편하게 작성하도록 도와주는 빌더 클래스일 뿐 결국 JPQL에 뿌리를 두고 있기 때문에 JPQL을 제대로 이해하지 못하면 위에 언급한 기술도 사용하기 어렵기 때문이다. JPQL JPQL의 특징 테이블이 아닌 객체를 대상으로 검색하는 객체지향 쿼리다. SQL을 추상화해서 특정 데이터베이스 SQL에 의존하지 않는다. JPQL 작성 시 유의사항 SELECT m..