beadss / jpa-study

jpa슽터디입니다
1 stars 2 forks source link

12장 정리 #35

Open 2xel opened 5 years ago

2xel commented 5 years ago

12. 스프링 데이터 JPA

1. 스프링 데이터 JPA 소개

2. 스프링 데이터 JPA 설정

@Configuration
@EnableJpaRepositories(basePackages = "jpabook.jpashop.repository")
public class appConfig {}

3. 공통 인터페이스 기능

// JpaRepository 공통 기능 인터페이스
public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
    ...
}
// JpaRepsository를 사용하는 인터페이스
public interface MemberRepsository extends JpaRepository<Member, Long> {
}

4. 쿼리 메소드 기능

4.1 메소드 이름으로 쿼리생성

4.2 JPA NamedQuery

// @NamedQuery 어노테이션으로 Named 쿼리 정의
@Entity
@NamedQuery(
    name = "Member.findByUsername",
    query = "select m from Member m where m.username = :username")
)
    public class Member {
        ...
    }
// JPA를 직접 사용해서 Named 쿼리 호출
public class MemberRepository {
    public List<Member> findByUsername(String username) {
        ...
        List<Member> resultList = 
            em.createNamedQuery("Member.findByUsername", Member.class)
                .setParameter("username", "회원1")
                .getResultList();
    }
}
// 스프링 데이터 JPA로 Named 쿼리 호출
public interface MemberRepository extends JpaRepository<Member, Long> {
    List<Member> findByUsername(@Param("username") String username);
}

4.3 @Query, 레포짓토리 메소드에 쿼리 정의

// 메소드에 JPQL 쿼리 작성
public interface MemberRepository extends JpaRepository<Member, Long> {
    @Query("select m from Member m where m.username = ?1")
    Member findByUsername(String username);
}

4.4 파라미터 바인딩

4.5 벌크성 수정 쿼리

// JPA를 사용한 벌크성 수정 쿼리
int bulkPriceUp(String stockAmount) {
    ...
    String qlString = "update Product p set p.price = p.price * 1.1 where p.stockAmount < :stockAmount";

    int resultCount = em.createQuery(qlString)
                        .setParamter("stockAmount", stockAmount)
                        .executeUpdate();
}
// 스프링 데이터 JPA를 사용한 벌크성 수정 쿼리
@Modifying
@Query("update Product p set p.price = p.price * 1.1 where p.stockAmount < :stockAmount")
int bulkPriceUp(@Param("stockAmount") String stockAmount);

4.6 반환 타입

4.7 페이징과 정렬

// count 쿼리 사용
Page<Member> findByName(String name, Pageable pageable);
// count 쿼리 사용 안함
List<Member> findByName(String name, Pageable pageable);

List<Member> findByName(String name, Sort sort);
// Page 사용 예제 실행 코드
// 페이징 조건과 정렬 조건 설정
PageRequery pageRequest = new PageRequest(0, 10, new Sort(Direction.DESC, "name"));
Page<Member> result = memberRepositry.findByStartingWith("김", pageRequery);

List<Member> members = result.getContent(); // 조회된 데이터
int totalPages = result.getTotalPages();    // 전체 페이지 수
boolean hasNextPage = result.hasNextPage(); // 다음 페이지 존재 여부

4.8 힌트

4.9 Lcok

@Lock(LockModeType.PESSIMITIC_WRITE)
List<Member> findByName(String name);

5. 명세

6. 사용자 정의 레포짓토리 구현

// 사용자 정의 인터페이스
public interface MemberRepositoryCustom{
    public List<Member> findMemberCustom();
}
// 사용자 정의 구현 클래스
public class MemberReposityImpl implements MemberRepositoryCustom {
    @Override
    public List<Member> findMemberCustom() {
        // 사용자 정의 구현
    }
}
// 사용자 정의 인터페이스 상속
public interface MemberRepository extends JpaRepository<Member, Long>, MemberRepositoryCustom {

}
@EnableJpaRepositories(basePackages = "jpabook.jpashop.repository", repositoryImplementationPostfix="Impl")

7. Web 확장

7.1 설정

7.2 도메인 클래스 컨버터 기능

// 회원의 아이디로 회원 엔티티 조회
@Controller
public class MemberController {
    @AutoWried MemberRepository memberRepositry;

    @RequestMapping("member/memberUpdateForm")
    public String memberUpdateForm(@Request("id") Long id, Model model) {
        Member member = memberRepository.findOne(id);
        model.addAttribute("member", member);
        return "member/memberSaveForm";
    }
}
// 도메인 클래스 컨버터 적용
@Controller
public class MemberController {
    @AutoWried MemberRepository memberRepositry;

    @RequestMapping("member/memberUpdateForm")
    public String memberUpdateForm(@Request("id") Long id, Model model) {
        model.addAttribute("member", member);
        return "member/memberSaveForm";
    }
}
// 페이징과 정렬 예제
@RequestMapping(value="/members", method=RequestMethod.GET)
public String list(Pageable pageable, Model model){
    Page<Member> page = memberService.findMembers(pageable);
    model.addAttribute("members", page.getContent());
    return "members/memberList";
}

접두사

public String list{
    @Qualifier("member") Pageable memberPageable,
    @Qualifier("order") Pageable orderPageable, ...
}

기본값

@RequestMapping(value="/member_page", method=RequestMethod.GET)
public String list(@PageableDefault(size=12, sort="name", direction=Sort.Direction.DESC) Pageable pageable) {
    ...
}

10. 스프링 데이터 JPA와 QueryDSL 통합

10.1 QueryDslPredicateExecutor 사용

public interfavce ItemRepository extends JpaRepository<Item, Long>, QueryDslPredicateExecutor<Item> {}
// QueryDSL 사용 예제
QItem item = QItem.item;
Iterable<Item> result = itemRepository.findAll(item.name.contains("장난감").and(item.price.between(1000,2000)));
// QueryDslPredicateExecutor 인터페이스
public interface QueryDslPredicateExecutor<T> {
    T findOne(Predicate predicate);
    Iterable<T> findAll(Predicate predicate);
    Iterable<T> findAll(Predicate predicate, OrderSpecifier<?>... order);
    Page<T> findAll(Predicate predicate, Pageable pageable);
    long count(Predicate predicate);
}

10.2 QueryDslRepositorySupport 사용

// CustomOrderRepository 사용자 정의 레파짓토리
public interface CustomOrderRepository {
    public List<Order> search(OrderSearch orderSearch);
}
public class OrderRepositoryImpl extends QueryDslRepositorySupport implements CustomOrderRepository {
    public OrderRepositoryImpl() {
        super(Order.class);
    }

    @Override
    public List<Order> search(OrderSearch orderSearch) {
        QOrder order = QOrder.order;
        QMember member = QMember.member;

        JPAQuery query = from(order);

        if(StringUtils.hasText(OrderSearch.getMemberName())) {
            query.leftJoin(order.member, member)
                .where(member.name.contains(orderSearch.getMemberName()));
        }

        if(orderSearch.getOrderStatus() != null) {
            query.where(order.status.eq(orderSearch.getOrderStatus()));''
        }

        return query.list(order);
    }
}
// QueryDslRepositorySupport 코드
@Repository
public abstract class QueryDslRepositorySupport {
    // 엔티티 매니저 반환
    protected EntityManager getEntityManager(){
        return EntityManager;
    }

    // from절 반환
    protected JPAQuery from(EntityPath<?>... paths) {
        return querydsl.createQuery(paths);
    }

    // QueryDSl delete 절 반환
    proected DeleteClause<JPADeleteClause> delete(EntityPat<?> path) {
       return new JPADeleteClause(entityManager, path);
    }

    // QueryDSL update 절 반환
    protected UpdateClause<JPAUpdateClause> update(EntityPath<?> path) {
        return new JPAUpdateClause(entityManger, path);
    }

    // 스프링 데이터 JPA가 제공하는 Querydsl를 편하게 사용하도록 돕는 헬퍼 객체 반환
    proected QueryDsl getQueryDsl() {
        return this.querydsl;
    }
}