스프링 데이터 JPA
스프링 데이터 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()로 초기화 해주지 않는다면
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);
@EntityGraph
패치조인을 간편하게 어노테이션으로 구현한 기능
간단하게 패치조인을 적용할 수 있다 → @EntityGraph를 사용
복잡하다 → JPQL 적용