org.springframework.data.jpa.repository.JpaRepository
)MemberRepository.findByUsername()
같은 메서드도public interface MemberRepository extends JpaRepository<Member, Long> {
}
Repository
, CrudRepository
, PagingAndSortingRepository
JpaRepository
스프링 데이터 JPA 가 제공하는 쿼리 메소드 기능 3가지
@Query
어노테이션을 이용해서 리포지토리 인터페이스에 쿼리 직접 정의//이메일과 이름으로 회원 조회
public interface MemberRepository extends Repository<Member, Long> {
List<Member> findByEmailAndName(String email, String name);
}
//JPQL
select m from Member m where m.email = ?1 and m.name = ?2
.
+ 메소드 이름으로 스프링 데이터 JPA는 Named Query를 찾는다.//NamedQuery 정의
@Entity
@NamedQuery(
name = "Member.findByUsername",
query = "select m from Member m where m.username = :username")
public class Member {
...
}
//NamedQuery 호출
public interface MemberRepository extends JpaRepository<Member, Long> {
List<Member> findByUsername(@Param("username") String username);
}
public interface MemberRepository extends JpaRepository<Member, Long> {
@Query("select m from Member m where m.username = ?1")
List<Member> findByUsername(String username);
}
@Param
으로 파라미터를 명시해준다.select m from Member m where m.username = ?1 //위치기반
select m from Member m where m.username = :username //이름기반
@Query("select m from Member m where m.username = :username")
List<Member> findByUsername(@Param("username") String username);
@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);
스프링 데이터 JPA는 쿼리 메소드에 페이징과 정렬을 사용할 수 있는 2가지 파라미터를 제공한다.
Sort
: 정렬 기능Pageable
: 페이징 기능(내부에 Sort 포함)// count 쿼리 사용
Page<Member> findByName(String name, Pageable pageable);
// count 쿼리 사용 안 함
List<Member> findByName(String name, Pageable pageable);
List<Member> findByName(String name, Sort sort);
public interface MemberRepository extends Repository<Member, Long> {
Page<Member> findByNameStartingWith(String name, Pageable pageable);
}
//실행코드
PageRequest pageRequest = new PageRequest(0, 10, new Sort(Direction.DESC, "name"));
Page<Member> result = memberRepository.findByNameStartingWith("김", pageRequest);
@QueryHint(name = "org.hibernate.readOnly", value = "true")
forCounting
: 반환타입으로 Page 인터페이스를 적용하면 추가로 호출하는 count 쿼리에도 쿼리 힌트를 적용할지 설정하는 옵션
@QueryHints(value = { @QueryHint(name = "org.hibernate.readOnly", value = "true")},
forCounting = true)
Page<Member> findByName(String name, Pageable pageable);
// 인터페이스 정의
public interface OrderRepository extends JpaRepository<Order, Long>,
JpaSpecificationExecutor<Order> {
}
// 명세 정의
public class OrderSpec {
public static Specification<Order> memberName(String memberName) {
return new Specification<Order>() {
public Predicate toPredicate(Root<Order> root,
CriteriaQuery<?> query, CriteriaBuilder builder) {
if (StringUtils.isEmpty(memberName)) return null;
Join<Order, Member> m = root.join("member", JoinType.INNER);
return builder.equal(m.get("name"), memberName);
}
}
};
// 위와 같은 방식으로 isOrderStatus() 구현
}
// 명세 사용
List<Order> result = orderRepository.findAll(
where(memberName(name)).and(isOrderStatus())
);
// 사용자 정의 인터페이스
public interface MemberRepositoryCustom {
public List<Member> findMemberCustom();
}
// 사용자 정의 구현 클래스
public class MemberRepositoryImpl implements MemberRepositoryCustom {
@Override
public List<Member> findMemberCustom() {
...
}
}
// 사용자 정의 인터페이스 상속
public interface MemberRepository extends JpaRepository<Member, Long>,
MemberRepositoryCustom {
}
HandlerMethodArgumentResolver
가@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
public class WebAppConfig {
}
RequestParam
으로는 분명 id
를 받고있는데 반환 객체는 Member
이다.
//회원 조회 기능
@Controller
public class MemberController {
@RequestMapping("member/memberUpdateForm")
public String memberUpdateForm(@RequestParam("id") Member member, Model model) {
model.addAttribute("member", member);
return "member/memberSaveForm";
}
}
HandlerMethodArgumentResolver
을 제공한다.PageableHandlerMethodArgumentResolver
SortHandlerMethodArgumentResolver
Pageable
객체를 받을 수 있다.@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";
}
SimpleJpaRepository
클래스가 구현한다.@Repository
적용: JPA 예외를 스프링이 추상화한 예외로 변환@Transactional
save()
@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, Id extends Serializable> implements
JpaRepository<T, ID>, JpaSpecificationExecutor<T> {
@Transactional
public <S extends T> S save(S entity) {
if (entityInformation.isnew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
...
}
스프링 데이터 JPA는 2가지 방법으로 QueryDSL을 지원한다.
org.springframework.data.querydsl.QueryDslPredicateExcutor
org.springframework.data.querydsl.QueryDslRepositorySupport
join
, fetch
등을 사용할 수 없다 (JPQL에서 이야기하는 묵시적 조인은 사용가능)JPAQuery
혹은 QueryDslRepositorySupport
를 사용해야 한다.public interface ItemRepository extends JpaRepository<Item, Long>,
QueryDslPredicateExecutor<Item> { }
QueryDslRepositorySupport
을 상속 받아 사용하면 편리하게 가능
@Repository
public abstract class QueryDslRepositorySupport {
// 엔티티 매니저 반환
protected EntityManager getEntityManager() {
return entityManager;
}
// from 절 반환
protected JPQLQuery from(EntityPath<?> ... paths) {
return querydsl.createQuery(paths);
}
// QueryDSL delete 절 바환
protected DeleteClause<JPADeleteClause> delete(EntityPath<?> path) {
return new JPADeleteClause(entityManager, path);
}
// QueryDSL update 절 반환
protected UpdateClause<JPAUpdateClause> update(EntityPath<?> path) {
return new JPAUpdateClause(entityManager, path);
}
// 스프링 데이터 JPA가 제공하는 Querydsl을 편하게 사용하도록 돕는 헬퍼 객체 반환
protected Querydsl getQuerydsl() {
return this.querydsl;
}
}