Spring Data JPA 사용하기

2025년 06월 10일
5

thumbnail.png
교과목 내용을 정리하기 위한 글입니다. 틀린 내용이 있을 수 있으니 참고하세요

1. 전통적 DAO 생성

JPA EntityManager를 직접 사용해 DAO를 작성하는 예시입니다.

@Repository
@Transactional
public class OfferDao {
 
    @PersistenceContext
    private EntityManager entityManager;
 
    public Product createProduct(Product product) {
        entityManager.persist(product);
        return product;
    }
 
    public Product findProduct(Long id) {
        return entityManager.find(Product.class, id);
    }
 
    public Product updateProduct(Product product) {
        return entityManager.merge(product);
    }
 
    public void deleteProduct(Long id) {
        Product product = findProduct(id);
        if (product != null) {
            entityManager.remove(product);
        }
    }
}

문제점

  • 엔티티마다 DAO 클래스를 반복 작성해야 함 (Product, Customer, Student 등)
  • Entity 타입과 기본 키 타입만 다를 뿐 코드가 유사함

2. Spring Data JPA 소개

Spring Data JPA를 사용하면 레포지토리 인터페이스만 정의해도 기본 CRUD 구현을 자동으로 제공합니다.

public interface ProductRepository
        extends CrudRepository<Product, Integer> {
}

위와 같이 선언하면 다음 메서드들이 자동 생성됩니다:

  • findAll()
  • findById(ID id)
  • save(S entity)
  • deleteById(ID id)

주요 레포지토리 인터페이스 계층

  • CrudRepository<T, ID>: CRUD 기본 기능 제공
  • PagingAndSortingRepository<T, ID>: 페이징·정렬 기능 추가
  • JpaRepository<T, ID>: 배치 삭제, 플러시 기능 등 JPA 확장 기능 제공

3. CrudRepository

CrudRepository가 제공하는 메서드 예시:

메서드설명
long count()엔티티 개수 조회
void delete(T entity)특정 엔티티 삭제
void deleteById(ID id)ID로 엔티티 삭제
Iterable<T> findAll()모든 엔티티 조회
Optional<T> findById(ID id)ID로 엔티티 조회
<S extends T> S save(S entity)엔티티 저장/수정
<S extends T> Iterable<S> saveAll(...)여러 엔티티 저장/수정

4. PagingAndSortingRepository

페이징과 정렬을 지원합니다.

public interface ProductRepository
    extends PagingAndSortingRepository<Product, Long> {
}

사용 예시:

@Service
public class ProductService {
    @Autowired
    private ProductRepository productRepository;
 
    public List<Product> findAllSortedByPrice() {
        Sort sort = Sort.by(Sort.Direction.DESC, "price");
        return (List<Product>) productRepository.findAll(sort);
    }
 
    public Page<Product> findByPage(int page, int size) {
        Pageable pageable = PageRequest.of(page, size,
            Sort.by(Sort.Direction.ASC, "name"));
        return productRepository.findAll(pageable);
    }
}
  • 페이지 번호는 0부터 시작
  • Page<T>를 통해 페이징 정보(총 페이지, 총 요소 등) 제공

5. JpaRepository

JpaRepositoryPagingAndSortingRepository를 상속하며, 추가로 다음 기능을 제공합니다:

  • void flush()
  • void deleteAllInBatch() 등 배치 삭제
  • List<T> findAll()의 리턴 타입이 List<T>
public interface ProductRepository
    extends JpaRepository<Product, Integer> {
}

6. Spring Data JPA 사용 예시

서비스 레이어 전통 방식

@Service
public class ProductService {
    @PersistenceContext
    private EntityManager entityManager;
 
    @Transactional
    public Product save(Product product) {
        if (product.getId() == null) {
            entityManager.persist(product);
            return product;
        } else {
            return entityManager.merge(product);
        }
    }
 
    public Product findById(Integer id) {
        return entityManager.find(Product.class, id);
    }
 
    @Transactional
    public void deleteById(Integer id) {
        Product product = findById(id);
        if (product != null) {
            entityManager.remove(product);
        }
    }
 
    public boolean existsById(Integer id) {
        return findById(id) != null;
    }
}

Spring Data JPA 적용

public interface ProductRepository
        extends JpaRepository<Product, Integer> {
}
 
@Service
@Transactional
public class ProductService {
    @Autowired
    private ProductRepository repo;
 
    public Product get(long id) {
        return repo.findById(id).orElse(null);
    }
 
    public List<Product> listAll() {
        return repo.findAll();
    }
 
    public void save(Product product) {
        repo.save(product);
    }
 
    public void delete(long id) {
        repo.deleteById(id);
    }
}

7. 쿼리 메소드(Query Methods)

메소드 이름만으로 동적 쿼리 생성이 가능합니다.

public interface MemberRepository
    extends JpaRepository<Member, Long> {
    List<Member> findByUsername(String username);
    List<Member> findByUsernameAndAgeGreaterThan(
        String username, int age);
}
  • findByXxxAndYyy, findByAgeLessThan, findByFirstnameContaining 등 다양한 키워드 지원
  • 반환 타입: List<T>, Page<T>, Optional<T>

8. @Query 어노테이션

메소드 이름으로 표현하기 어려운 복잡한 쿼리는 @Query로 JPQL 또는 네이티브 SQL을 직접 지정할 수 있습니다.

public interface BoardRepository
    extends JpaRepository<Board, Long> {
 
    @Query("SELECT b FROM Board b WHERE b.title LIKE %:kw% " +
           "ORDER BY b.seq DESC")
    List<Board> searchByTitle(@Param("kw") String keyword);
 
    @Query(value = "SELECT seq, title FROM board " +
                   "WHERE title LIKE %?1% ORDER BY seq DESC",
           nativeQuery = true)
    List<Object[]> searchNative(String keyword);
}
  • JPQL: 엔티티명, 필드명 사용 (대소문자 구분)
  • 위치 기반(?1), 이름 기반(:param) 파라미터 바인딩 지원