경희대학교 컴퓨터공학부 하계 리턴 백엔드(스프링부트) 스터디 4주차 - 트랙장 최현영
JPQL(JPA Query Language)
select m from member m
⇒ 여기서 member는 엔티티 타입이고, m이라는 별칭으로 지정해 주었고, 해당 엔티티의 모든 필드를 출력하도록 JPQL을 작성하였다.
쿼리 메서드 살펴보기
JpaRepository 인터페이스는 상속만 해줘도 이미 정의되고 구현된 CRUD 메서드를 호출하여 사용할 수 있었다.
이렇듯, 개발자의 입맛대로 쿼리문을 정의할 수 있도록 키워드 몇개를 조합하여 하나의 메서드를 생성할 수 있는데 이를 쿼리 메서드라고 부른다.
쿼리 메서드 작성은 Jpa리파지토리 인터페이스를 상속받은, 사용자 정의 인터페이스의 블록 내부에서 작성한다.
크게 동작을 결정하는 주제와 서술어로 구분한다.
리턴타입 { 주제 + 서술어}(대상 필드)
List<Member> findByClub(String club)
자세한 쿼리는 다음 기술 블로그를 참고
주제 키워드
find(All)By…
//findBy...
Optional<Member> findByName(Stirng name);
Member findByClub(String club);
List<Member> findAllByGeneration(String generation);
b. existBy…
//existsBy...
boolean existsByName(String name);
c. countBy…
Long countByGeneration(String generation);
d. deleteBy
삭제해줘
삭제를 수행하는 쿼리
void deleteById(Long Id); //반환값 없음
e. …First<Number>… / …Top<Number> …
몇 개만 추려줘
쿼리를 통해 조회딘 결과값의 갯수를 제한함
Number는 몇개까지 조회할 것인지를 표현하는 것으로 오직 단일의 엔티티 인스턴스만 조회하고자 한다면 생략 가능
List<Member> findFirst3ByGeneration(String generation);
//처음 상위 3건만 조회하고자 함
List<Member> findTop5ByGeneration(String generation);
//상위 5건만 조회하고자 함
f. Distinct
중복 제거
List<Member> findDistinctByXXX();
조건자 키워드
//FindBy..와 동일하게 적용됨
Member findByClubIs(String club);
Member findByClubEquals(String club);
b. IsNot
Member findByClubIsNot(String club);
c. IsNull / IsNotNull
List<Member> findAllByClubIsNull(String club, String generation);
List<Member> findAllByClubIsNotNull(String club);
d. IsTrue / isFalse
e. And / Or
f. IsGreaterThan(Equal)[after] / IsLessThan(Equal)[before] / IsBetween
g. IsStartingWith / IsEndingWith / IsContaining
Order By
List<Member> findByClubOrderByGenerationAsc(String club);
List<Member> findByClubOrderByGenerationDesc(String club);
d. 여러 정렬 조건을 주고 싶다면 And와 Or 키워드는 사용하지 않음
List<Member> findByClubOrderByGenerationAscStudentIdAsc(String club);
List<Member> findByClubOrderByGenerationDescStudentIdDesc(String club)
e. Sort 객체 및 Order 객체 활용 ⇒ 매개변수 쿼리 정렬
//리파지토리 인터페이스 내
List<Member> findByClub(String club, Sort sort);
//서비스 클래스 내
List<Member> members = memberRepository.findByName("RETURN",
Sort.by(Order.asc("generation"));
//두개 이상의 정렬 조건인 경우 Sort.by(Order.by(~) , ...);
Page 및 Pageable
//리파지토리 인터페이스 내
Page<Member> findByClub(String club, Pageable pageable);
//반환값은 Page 타입로 두고, 입력 파라미터로 Pageable 타입으로 두자,
//서비스 클래스 내
Page<Member> memberPage = memberRepository.findByClub("RETURN",
PageReqeust.of(2,6));
@Query
@Query("SELECT m FROM MEMBER AS m WHERE m.club = :club")
List<Member> findByClub(@Param("club") String club);
⇒ AS 문을 통해 Member 엔티티를 m이라는 별칭을 두었다.
⇒ @Query 어노테이션의 JPQL 중, 조건문에서 ‘:’ 은 이름 기반으로 입력 파라미터의 값을 가져오기 위한 파리미터 바인딩을 해주는 문법에 해당한다. 즉, club이라는 입력 파라미터에 “RETURN”이라는 문자열이 들어오면 @Param에 정의한 club에 바인딩하라고 지시하고, ‘:club’에 “RETURN”을 대입하는 것이다.
연관관계
1:1
1:N
N:1
N:M
데이터베이스에서는 두 테이블의 연관관계를 설정하면 외래키만으로 서로 조인(JOIN)을 통해 참조하는 구조로 생성되지만, JPA를 사용하는 객체지향 모델링에서는 엔티티 간 참조 방향을 설정할 수 있다.
⇒ 한 테이블에서 다른 테이블의 기본값을 외래키로 가지게 된다. → 외래키를 가지는 테이블이 그 관계에서 주인이 된다.
8.1 applicatoin.yml에서 opne-in-view 옵션을 false로 설정하자[매우 중요]
jpa:
hibernate:
ddl-auto: create
properties:
hibernate:
show_sql: true
format_sql: true
**open-in-view: false**
1:1 (단방향)
package com.rebe.returnstudy.Entity;
import jakarta.persistence.*;
import lombok.*;
@Entity
@Getter
@Setter
**@ToString //추가해 주자**
@NoArgsConstructor
@AllArgsConstructor
public class Member {
..
package com.rebe.returnstudy.Entity;
import jakarta.persistence.*;
import lombok.*;
@Entity
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class MemberDetails {
@Id
@GeneratedValue
private Long id;
@Column(unique = false, nullable = false)
private String email;
@Column(unique = false, nullable = false)
private String phoneNumber;
@Column(nullable = true, length = 255)
private String StatusMsg;
@Column
private boolean isActive;
**@OneToOne
@JoinColumn(name = "member_id")
private Member member;**
}
@OneToOne
fetch
optional
mappedBy
@JoinColumn
package com.rebe.returnstudy.Repository;
import com.rebe.returnstudy.Entity.MemberDetails;
import org.springframework.data.jpa.repository.JpaRepository;
public interface MemberDetailsRepository extends JpaRepository<MemberDetails, Long> {
}
----
package com.rebe.returnstudy.Service;
import com.rebe.returnstudy.Entity.Member;
import com.rebe.returnstudy.Entity.MemberDetails;
import com.rebe.returnstudy.Repository.MemberDetailsRepository;
import com.rebe.returnstudy.Repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Service
**@RequiredArgsConstructor**//final 한정자로 된 필드를 의존성 주입하기 위해 어노테이션 추가
**@Slf4j**//로그 출력을 위해 사용될 어노테이션 추가
public class MemberDetailsService {
private final MemberRepository memberRepository;
private final MemberDetailsRepository memberDetailsRepository;
public void OneToOne(){
Member member = new Member();
member.setStudentId(2019102236);
member.setName("최현영");
member.setGeneration("32nd");
member.setClub("RETURN");
memberRepository.save(member);
MemberDetails memberDetails = new MemberDetails();
memberDetails.setMember(member); //위에서 저장한 member 인스턴스를 setter 메서드로 할당
memberDetails.setActive(true);
memberDetails.setEmail("[email protected]");
memberDetails.setPhoneNumber("01095026088");
memberDetails.setStatusMsg("날씨 미친거 아님? 너무 덥다.");
memberDetailsRepository.save(memberDetails);
log.info("saved Member Entity" + memberDetailsRepository.findById(memberDetails.getId())
.get().getMember());
log.info("saved Member Details Entity : " + memberDetailsRepository.findById(memberDetails.getId())
.get());
}
}
----
package com.rebe.returnstudy.Controller;
import com.rebe.returnstudy.Service.MemberDetailsService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
public class OneToOneController {
private final MemberDetailsService memberDetailsService;
@GetMapping("/one-to-one")
public void OneToOneTest(){
memberDetailsService.OneToOne();
}
}
단방향 매핑이기 때문에 외래키를 관리하지 않는다.
외래키를 관리하고 있다.
1:1 (양방향)
N:1
1:N
N:M
영속성 전이[CasCade]
고아[Orphan]