Open skarltjr opened 3 years ago
select m from Member m where m.username = ?0 //위치 기반
select m from Member m where m.username = :name //이름 기반
public interface MemberRepository extends JpaRepository<Member, Long> {
@Query("select m from Member m where m.username = :name")
Member findMembers(@Param("name") String username);
}
@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 interface MemberRepository extends Repository<Member, Long> {
Page<Member> findByAge(int age, Pageable pageable);
}
PageRequest pageRequest = PageRequest.of(0, 3, Sort.by(Sort.Direction.DESC,
"username"));
Page
- 페이지는 0부터
- 만약 페이징한 엔티티를 DTO로 가져간다면
Page
#### 참고로 카운트쿼리를 따로 뺄 수 있는데 . 복잡한sql에선 카운트쿼리가 매우 무겁기때문에 따로빼주는게 중요!
@Query(value = “select m from Member m”,
countQuery = “select count(m.username) from Member m”)
Page
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
가장 큰 문제점은 벌크연산은 영속성컨텍스트를 무시하고 실행(바로 db에 적용된다) - > 영속성컨텍스트에 있는 엔티티와 db엔티티의 상태가 달라질 수 있다. 따라서 벌크연산 후 자동으로 영속성컨텍스트를 초기화하도록
ex) jpa는 db에 접근하기전 영속성컨텍스트에서 먼저 확인을 하기 때문에 만약 조회하고자하는 내용을 영속성컨텍스트에서 가져올 수 있다면 영속성컨텍스트에서 가져온다 - 이 때 만약 벌크연산을 했다면 벌크연산은 db에 바로적용되기 때문에 영속성컨텍스트에 있는 내용과 실제db의 값이 달라질 수 있다. 따라서 이 경우에 영속성컨텍스트 초기화를 하지않고 조회를 한다면 db에 있는 새로운 값이 아닌 벌크연산 전 영속성컨텍스트에 존재하는 이전값을 가져오게된다.
벌크성 쿼리를 실행하고 나서 영속성 컨텍스트 초기화: @Modifying(clearAutomatically = true)
되도록이면 영속성컨텍스트에 엔티티가 없는 상태에 벌크연산
혹은 벌크연산 직후 영속성컨텍스트초기화해주기
@Modifying(clearAutomatically = true)
@Query("update Member m set m.age = m.age + 1 where m.age >= :age")
int bulkAgePlus(@Param("age") int age);
페치조인
@Query("select m from Member m left join fetch m.team")
List<Member> findMemberFetchJoin();
엔티티그래프
@NamedEntityGraph(name = "Study.withManagersAndMembers",attributeNodes = {
@NamedAttributeNode("managers"),
@NamedAttributeNode("members")
})
@Entity @Builder
@Getter @Setter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = "id")
public class Study {
@Id @Column(name = "study_id")
@GeneratedValue
private Long id;
@ManyToMany
private Set<Account> managers = new HashSet<>();
@ManyToMany
private Set<Account> members = new HashSet<>();
등등
- 사용
@EntityGraph(value = "Study.withManagersAndMembers",type = EntityGraph.EntityGraphType.FETCH) Study findWithAllMemberByPath(String path);
#### 스프링데이터jpa의 확장기능
규칙: 리포지토리 인터페이스 이름 + Impl
스프링 데이터 JPA가 인식해서 스프링 빈으로 등록
- Querydsl에서 사용예시
1. 스터디레포지토리
@Transactional(readOnly = true) public interface StudyRepository extends JpaRepository<Study,Long>,StudyRepositoryExtension {
2. 커스텀스터디레퍼지토리
@Transactional(readOnly = true) public interface StudyRepositoryExtension {
Page<Study> findByKeyword(String keyWord, Pageable pageable);
List<Study> findByAccountWithTagsAndZones(Account current);
}
3. 2번 커스텀스터디레퍼지토리를 구현하는 Impl
public class StudyRepositoryExtensionImpl extends QuerydslRepositorySupport implements StudyRepositoryExtension { public StudyRepositoryExtensionImpl() { super(Study.class); }
@Override
public Page<Study> findByKeyword(String keyWord, Pageable pageable) {
JPQLQuery<Study> query = from(study)
.where(study.published.isTrue()
.and(study.title.containsIgnoreCase(keyWord))
.or(study.tags.any().title.containsIgnoreCase(keyWord))
.or(study.zones.any().localNameOfCity.containsIgnoreCase(keyWord)))
.leftJoin(study.tags, tag).fetchJoin()
.leftJoin(study.zones, zone).fetchJoin()
.distinct();
JPQLQuery<Study> studyJPQLQuery = getQuerydsl().applyPagination(pageable, query);
QueryResults<Study> result = studyJPQLQuery.fetchResults();
return new PageImpl<>(result.getResults(), pageable, result.getTotal());
}
@Override
public List<Study> findByAccountWithTagsAndZones(Account current) {
JPQLQuery<Study> query = from(study)
.where(study.tags.any().in(current.getTags())
.or(study.zones.any().in(current.getZones()))
.and(study.published.isTrue()))
.leftJoin(study.tags, tag).fetchJoin()
.leftJoin(study.zones, zone).fetchJoin()
.orderBy(study.publishedDateTime.desc())
.limit(9)
.distinct();
return query.fetch();
}
}
@GetMapping("/members")
public Page<MemberDto> list(Pageable pageable) {
Page<Member> page = memberRepository.findAll(pageable);
Page<MemberDto> pageDto = page.map(MemberDto::new);
return pageDto;
}
JPA를 사용한 레퍼지토리
spring data jpa를 사용하면?
자주쓰이는 findById 등 CRUD가 인터페이스에 이미 구성되어있고 그걸 활용하면된다.
스프링 데이터 jpa의 쿼리 메소드 기능 - 매서드이름을 통해
필드명이 변하면 매서드의 이름도 바꿔줘야한다 -> 컴파일시점에 에러를 잡아낼 수 있는 큰 장점
사용예시
spring data jpa의 NamedQuery
@Query를 통해 매소드에 쿼리 정의하기