JpaRepository 상속 받은 interface 작성 public interface MemberRepository extends JpaRepository<Member, Long> {
}

em.createQuery() 사용@NamedQuery 어노테이션으로 Named 쿼리 정의@Entity
@NamedQuery( name="Member.findByUsername",
query="select m from Member m where m.username = :username")
public class MemberRepository {
public List<Member> findByUsername(String username) {
...
List<Member> resultList =
em.createNamedQuery("Member.findByUsername", Member.class)
.setParameter("username", username)
.getResultList();
}
}
@Query가 없을 경우 {도메인 클래스}.{메서드 이름}으로 찾음. @Param필요 @Query(name = "Member.findByUsername")
List<Member> findByUsername(@Param("username") String username);
@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);
@Query("select m.username from Member m")
List<String> findUsernameList();
@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 //이름 기반
@Query("select m from Member m where m.username in :names")
List<Member> findByNames(@Param("names") List<String> names);
List<Member> findByUsername(String name); //컬렉션
Member findByUsername(String name); //단건
Optional<Member> findByUsername(String name); //단건 Optional
public List<Member> findByPage(int age, int offset, int limit) {
return em.createQuery("select m from Member m where m.age = :age order by
m.username desc") // 정렬 로직 존재
.setParameter("age", age) // 검색 조건
.setFirstResult(offset) // 시작 지점
.setMaxResults(limit) // 개수
.getResultList(); // 결과
}
public long totalCount(int age) {
return em.createQuery("select count(m) from Member m where m.age = :age",
Long.class)
.setParameter("age", age)
.getSingleResult();
org.springframework.data.domain.Sort : 정렬 기능 org.springframework.data.domain.Pageable : 페이징 기능 (내부에 Sort 포함)org.springframework.data.domain.Page : 추가 count 쿼리 결과를 포함하는 페이징org.springframework.data.domain.Slice : 추가 count 쿼리 없이 다음 페이지만 확인 가능Page<Member> findByUsername(String name, Pageable pageable); //count 쿼리 사용 Slice<Member> findByUsername(String name, Pageable pageable); //count 쿼리 사용 안함
List<Member> findByUsername(String name, Pageable pageable); //count 쿼리 사용 안함
List<Member> findByUsername(String name, Sort sort);
public interface Page<T> extends Slice<T> {
int getTotalPages(); //전체 페이지 수
long getTotalElements(); //전체 데이터 수
<U> Page<U> map(Function<? super T, ? extends U> converter); //변환기
}
public interface Slice<T> extends Streamable<T> {
int getNumber(); // 현재 페이지
int getSize(); // 페이지 크기
int getNumberOfElements(); // 현재 페이지에 나올 데이터 수
List<T> getContent(); // 조회된 데이터
boolean hasContent(); // 조회된 데이터 존재 여부
Sort getSort(); // 정렬 정보
boolean isFirst(); // 현재 페이지가 첫 페이지 인지 확인
boolean isLast(); // 현재 페이지가 마지막 페이지 인지 확인
boolean hasNext(); // 다음 페이지 존재 여부
boolean hasPrevious(); // 이전 페이지 존재 여부
Pageable getPageable(); // 다음 페이지 요청 정보
Pageable nextPageable(); / 다음 페이지 객체
Pageable previousPageable();//이전 페이지 객체
<U> Slice<U> map(Function<? super T, ? extends U> converter); // 변환기
}
@Query(value = “select m from Member m”, countQuery = “select count(m.username) from Member m”)
Page<Member> findMemberAllCountBy(Pageable pageable);
Page<Member> page = memberRepository.findByAge(10, pageRequest);
Page<MemberDto> dtoPage = page.map(m -> new MemberDto());
public int bulkAgePlus(int age) {
int resultCount = em.createQuery(
"update Member m set m.age = m.age + 1" +
"where m.age >= :age")
.setParameter("age", age)
.executeUpdate();
return resultCount;
}
@Modifying 어노테이션을 사용org.hibernate.hql.internal.QueryExecutionRequestException: Not supported for DML operations 에러 발생 em.flush() + em.clear()로 영속성 컨텍스트에 있는 값을 DB에 적용시켜야함. @Modifying(clearAutomatically = true) 다음과 같이 해도 em.flush() + em.clear()와 같은 효과를 가짐. @Modifying
@Query("update Member m set m.age = m.age + 1 where m.age >= :age")
int bulkAgePlus(@Param("age") int age);
@EntityGraph -> JPQL 없이 페치 조인 가능//공통 메서드 오버라이드
@Override
@EntityGraph(attributePaths = {"team"})
List<Member> findAll();
//JPQL + 엔티티 그래프
@EntityGraph(attributePaths = {"team"})
@Query("select m from Member m")
List<Member> findMemberEntityGraph();
//메서드 이름으로 쿼리에서 특히 편리하다.
@EntityGraph(attributePaths = {"team"})
List<Member> findByUsername(String username)
@NamedEntityGraph(name = "Member.all", attributeNodes =@NamedAttributeNode("team"))
@EntityGraph("Member.all")
@Query("select m from Member m")
List<Member> findMemberEntityGraph();@QueryHints(value = @QueryHint(name = "org.hibernate.readOnly", value ="true")) //
Member findReadOnlyByUsername(String username);
@Lock(LockModeType.PESSIMISTIC_WRITE)
List<Member> findByUsername(String name);
사용이 필요한 경우
사용 순서
@PrePersist@PostPersist@PreUpdate@PostUpdate @MappedSuperclass
@Getter
public class JpaBaseEntity {
@Column(updatable = false)
private LocalDateTime createdDate;
private LocalDateTime updatedDate;
@PrePersist
public void prePersist() {
LocalDateTime now = LocalDateTime.now();
createdDate = now;
updatedDate = now;
}
@PreUpdate
public void preUpdate() {
updatedDate = LocalDateTime.now();
}
}
@EnableJpaAuditing@CreatedDate@LastModifiedDate@CreatedBy@LastModifiedBy@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
@Getter
public class BaseEntity {
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdDate;
@LastModifiedDate
private LocalDateTime lastModifiedDate;
}
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
public class BaseEntity {
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdDate;
@LastModifiedDate
private LocalDateTime lastModifiedDate;
@CreatedBy
@Column(updatable = false)
private String createdBy;
@LastModifiedBy
private String lastModifiedBy;
@Bean
public AuditorAware<String> auditorProvider() {
return () -> Optional.of(UUID.randomUUID().toString());
}
@GetMapping("/members/{id}")
public String findMember(@PathVariable("id") Long id) {
Member member = memberRepository.findById(id).get();
return member.getUsername();
}
@GetMapping("/members/{id}")
public String findMember(@PathVariable("id") Member member) {
return member.getUsername();
}
@GetMapping("/members")
public Page<Member> list(Pageable pageable) {
Page<Member> page = memberRepository.findAll(pageable);
return page;
}
요청 파라미터
설정 기본 값
spring.data.web.pageable.default-page-size=20 /# 기본 페이지 사이즈
spring.data.web.pageable.max-page-size=2000 /# 최대 페이지 사이즈/
글로벌
data:
web:
pageable:
default-page-size: n
max-page-size: m
개별 변경
@PageableDefault 사용 @RequestMapping(value = "/members_page", method = RequestMethod.GET)
public String list(@PageableDefault(size = 12, sort = “username”, direction = Sort.Direction.DESC) Pageable pageable) {
...
}
DTO 사용
page.map(classDto::new)page 1 부터 시작하기
@Transactional 트랜잭션 적용
@Transactional(readOnly = true
readOnly = true 사용하면 flush()를 생략해서 약간의 성능 향상을 얻을 수 있음save()
persist)merge)@GenerateValue -> save() 시점 식별자가 없으므로 새로운 entity로 판단.@Id + 직접 할당 -> save() 시점에 직접 할당으로 인해 이미 존재하는 entity로 판. -> merge()Persistable 를 사용해서 새로운 entity 확인 여부를 직접 구현JpaSpecificationExecutor 인터페이스 상속장점
단점
조인은 가능하지만 내부 조인(INNER JOIN)만 가능함 외부 조인(LEFT JOIN) 안됨
다음과 같은 중첩 제약조건 안됨
매칭 조건이 매우 단순함
자세한 내용은 필요할 때 찾아볼 것
entity 대신에 DTO를 편리하게 조회할 때 사용
데이터 조회에서의 Projections : select에 들어가는 필드
인터페이스 기반 closed Proejctions
public interface UsernameOnly {
String getUsername();
}List<UsernameOnly> result = memberRepository.findProjectionsByUsername("m1");인터페이스 기반 Open Proejctions
public interface UsernameOnly {
@Value("#{target.username + ' ' + target.age + ' ' + target.team.name}")
String getUsername()
}
클래스 기반 Projection
public class UsernameOnlyDto {
private final String username;
public UsernameOnlyDto(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
}
동적 Projections
<T> List<T> findProjectionsByUsername(String username, Class<T> type);
중첩 구조 처리
public interface NestedClosedProjection {
String getUsername();
TeamInfo getTeam();
interface TeamInfo {
String getName();
}
}
select
m.username as col_0_0_,
t.teamid as col_1_0_,
t.teamid as teamid1_2_,
t.name as name2_2_
from
member m
left outer join
team t
on m.teamid=t.teamid
where
m.username=?
주의