2 minute read

스프링 데이터 JPA

NamedQuery

@NamedQuery(
        name = "Member.findByUsername",
        query = "select m from Member m where m.username =:username"
)
public class Member {

    @Id
    @GeneratedValue
    @Column(name = "member_id")
    private Long id;
    private String username;
    private int age;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "team_id")
    private Team team;
	  ...
}
public interface MemberRepository extends JpaRepository<Member,Long> {
    @Query(name = "Member.findByUsername")
    List<Member> findByUsername(@Param("username")String username);
}

@Param(”username”)은 query = "select m from Member m where m.username =:username" 과 같이 username이란 이름을 지정해줘야 매핑이 된다.

네임드 쿼리는 정적쿼리이기때문에 애플리케이션 실행 시점에 IDE가 문법의 파싱 오류를 잡아준다.

실무에서는 사용이 되지 않는다.

@Query, 리포지 메소드 쿼리

Named쿼리보다 강력한기능 메서드에 쿼리를 바로 적용할 수 있다.

@Query("select m from Member m where m.username = :username and m.age = :age")
List<Member> findUser(@Param("username") String username,@Param("age") int age);

비교해보자

List<Member> findByUsernameAndAgeGreaterThan(String username,int age);

맨 처음에 사용했던 Jpa레포지토리의 코드이다 가독성이좋고 사용이 편리하다는 장점이있지만 지금 코드와같이 메서드명이 길어지면서 장점이 사라진다.

실무에서 메서드명이 적다면 기본 레포지토리 인터페이스 코드를 작성해서 사용하면되고, 복잡해지게되면 @Query문을 사용한다.

DTO로 조회시

DTO클래스를 하나 만들어 준다음

@Data
public class MemberDto {

    private Long id;
    private String username;
    private String teamName;

    public MemberDto(Long id, String username, String teamName) {
        this.id = id;
        this.username = username;
        this.teamName = teamName;
    }
}

new 생성자를 이용하여 패키지 경로와 함께 MemberDto에 생성인자를 집어넣어 조회하는 방식

@Query("select new study.datajpa.dto.MemberDto(m.id,m.username,t.name) from Member m join m.team t")
List<MemberDto> findMemberDto();

파라미터 바인딩

위치기반 → 거의 사용하지않음/ 파라미터 위치 변하면 에러발생

이름 기반 위주로 사용

select m from Member m where m.username = ?0 // 위치 기반
select m from Member m where m.username = :name // 이름 기반

반환 타입

반환타입에 컬렉션으로 설정했는데 데이터 베이스에 찾고자하는 파리미터가 없다면 null을 반환해야하는데 그렇지않는다.

List<Member> findListByUsername(String username); // 컬렉션
Member findMemberByUsername(String username); // 단건
Optional<Member> findOptionalByUsername(String username); //단건 Optional

데이터 베이스에 결과가 있을 수도 없을수도 있다면 자바8부터 제공하는 Optional<>타입을 사용하면 좋다.

벌크성 수정 쿼리

ex) 모든 직원의 월급을 10%이상 인상 할때, DB로 처리하는 예시

벌크 연산주의점:

벌크 연산은 영속성컨텍스트 → DB 반영 과정이 아니라 DB로 바로 접근하기 때문에 영속성 컨텍스트에 변경되지 않은 엔티티가 존재한다. 그렇기 때문에 em.flush()로 영속성 컨텍스틀 초기화 하여 변경된 데이터를 반영하도록 수정해야한다.

@Test
    public void bulkUpdate(){
        memberRepository.save(new Member("member1",10));
        memberRepository.save(new Member("member2",20));
        memberRepository.save(new Member("member3",30));
        memberRepository.save(new Member("member4",40));
        memberRepository.save(new Member("member5",50));

        int resultCount=memberRepository.bulkAgePlus(20);
        //em.clear(); 영속성컨텍스트 초기화

        List<Member> result=memberRepository.findByUsername("member5");
        Member member=result.get(0);
        System.out.println("member = " + member);

    }

em.clear()로 초기화 해주지 않는다면

jpa

DB에는 age=51로 영속성 컨텍스트 age=50으로 저장되어있게된다.

em.clear() 설정 또는 @Modifying(clearAutomatically = true) 설정을 통해 자동으로 영속성 컨텍스트를 초기화 시켜줄 수 있다.

'''MemberRepository 코드'''

@Modifying(clearAutomatically = true)
@Query("update Member m set m.age = m.age+1 where m.age >= :age")
int bulkAgePlus(@Param("age")int age);

jpa

@EntityGraph

패치조인을 간편하게 어노테이션으로 구현한 기능

간단하게 패치조인을 적용할 수 있다 → @EntityGraph를 사용

복잡하다 → JPQL 적용

Updated: