API/JPA

스프링 데이터 JPA

시뻘건 튼튼발자 2024. 9. 8. 12:05
반응형

스프링 데이터 JPA 소개

스프링 데이터는 다양한 데이터 저장소와의 통합을 지원하는 스프링 프로젝트다. 스프링 데이터 JPA는 그 중 하나로, JPA를 사용하여 데이터베이스와의 상호작용을 단순화하고, 개발자의 생산성을 높여준다. 주요 목표는 다음과 같다.

  • 데이터 접근 기술의 추상화: JPA를 사용하는 데이터 접근 기술을 더 간편하게 사용하도록 돕는다.
  • 표준화된 데이터 접근 레이어: 데이터 접근 계층의 표준화와 코드의 재사용성을 높인다.
  • 쿼리 메소드와 자동화: 쿼리 메소드, JPQL, 네이티브 쿼리 등을 사용하여 데이터 접근을 간단히 한다.

스프링 데이터 JPA는 JpaRepository와 같은 인터페이스를 제공하여 CRUD 작업과 데이터 조회를 간편하게 수행할 수 있게 한다.

스프링 데이터 JPA 설정

스프링 데이터 JPA를 설정하려면 다음과 같은 기본 단계를 따른다.

 

의존성 추가: pom.xml 또는 build.gradle 파일에 스프링 데이터 JPA와 JPA 구현체(예: Hibernate) 의존성을 추가한다.

<!-- Maven -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

// Gradle
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

 

애플리케이션 설정: application.properties 또는 application.yml 파일에 데이터베이스 연결 설정을 추가한다.

# application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/mydatabase
spring.datasource.username=root
spring.datasource.password=password
spring.jpa.hibernate.ddl-auto=update

 

 

공통 인터페이스 기능

스프링 데이터 JPA는 여러 공통 인터페이스를 제공한다. 주요 인터페이스는 다음과 같다.

  • CrudRepository: 기본적인 CRUD 작업을 제공하는 인터페이스입니다. save(), findById(), findAll(), deleteById() 등의 메소드를 포함한다.
    • public interface CrudRepository<T, ID> {
          <S extends T> S save(S entity);
          Optional<T> findById(ID id);
          Iterable<T> findAll();
          void deleteById(ID id);
      }
  • PagingAndSortingRepository: 페이징 및 정렬 기능을 추가로 제공하는 인터페이스다.
    • public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
          Page<T> findAll(Pageable pageable);
          Iterable<T> findAll(Sort sort);
      }
  • JpaRepository: JPA 전용 기능을 추가로 제공하는 인터페이스입니다. flush(), saveAndFlush(), findAll(Specification<T> spec) 등의 메소드를 포함한다.
    • public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID> {
          void flush();
          <S extends T> S saveAndFlush(S entity);
          List<T> findAll(Specification<T> spec);
      }

쿼리 메소드 기능

스프링 데이터 JPA는 리포지토리 메소드 이름을 기반으로 쿼리를 자동으로 생성한다.

메소드 이름으로 쿼리 생성

리포지토리 메소드의 이름에 따라 쿼리를 자동으로 생성한다. 예를 들어, findByLastName이라는 메소드는 lastName 필드를 기준으로 사용자 엔티티를 조회한다.

public interface UserRepository extends JpaRepository<User, Long> {
    List<User> findByLastName(String lastName);
}

JPA NamedQuery

NamedQuery를 사용하여 JPQL 쿼리를 미리 정의하고 사용할 수 있다.

@Entity
@NamedQuery(name = "User.findByLastName", query = "SELECT u FROM User u WHERE u.lastName = :lastName")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String lastName;
    // other fields, getters and setters
}

public interface UserRepository extends JpaRepository<User, Long> {
    @Query(name = "User.findByLastName")
    List<User> findByLastName(@Param("lastName") String lastName);
}

@Query, 리파지토리 메소드에 쿼리 정의

@Query 어노테이션을 사용하여 메소드에 JPQL 또는 네이티브 쿼리를 직접 정의할 수 있다.

public interface UserRepository extends JpaRepository<User, Long> {
    @Query("SELECT u FROM User u WHERE u.email = :email")
    User findByEmail(@Param("email") String email);
}

 

파라미터 바인딩

쿼리 메소드에 파라미터를 바인딩하여 쿼리를 동적으로 생성할 수 있다. @Param 어노테이션을 사용하여 쿼리 파라미터에 값을 전달한다.

public interface UserRepository extends JpaRepository<User, Long> {
    @Query("SELECT u FROM User u WHERE u.name = :name AND u.email = :email")
    User findByNameAndEmail(@Param("name") String name, @Param("email") String email);
}

 

벌크성 수정 쿼리

벌크성 수정 쿼리를 작성하여 한 번의 쿼리로 다수의 엔티티를 업데이트하거나 삭제할 수 있다.

public interface UserRepository extends JpaRepository<User, Long> {
    @Modifying
    @Query("UPDATE User u SET u.status = :status WHERE u.lastLogin < :date")
    int updateStatusForInactiveUsers(@Param("status") String status, @Param("date") LocalDate date);
}

 

반환 타입

쿼리 메소드의 반환 타입은 List, Optional, Page, Slice 등 다양한 형태를 가질 수 있다.

public interface UserRepository extends JpaRepository<User, Long> {
    List<User> findByLastName(String lastName);
    Optional<User> findById(Long id);
    Page<User> findAll(Pageable pageable);
}

 

페이징과 정렬

페이징과 정렬을 위한 기능을 지원한다. Pageable 객체를 사용하여 페이징과 정렬을 적용할 수 있다.

public interface UserRepository extends JpaRepository<User, Long> {
    Page<User> findByLastName(String lastName, Pageable pageable);
}

힌트

JPA 쿼리의 성능을 조정하기 위해 힌트를 사용할 수 있다.

public interface UserRepository extends JpaRepository<User, Long> {
    @Query("SELECT u FROM User u WHERE u.lastName = :lastName")
    @QueryHints(@QueryHint(name = "org.hibernate.readOnly", value = "true"))
    List<User> findByLastName(@Param("lastName") String lastName);
}

 

Lock

쿼리 메소드에 @Lock 어노테이션을 사용하여 엔티티에 대해 동시성 제어를 할 수 있다.

public interface UserRepository extends JpaRepository<User, Long> {
    @Lock(LockModeType.PESSIMISTIC_WRITE)
    @Query("SELECT u FROM User u WHERE u.id = :id")
    User findByIdForUpdate(@Param("id") Long id);
}

 

Specifications(명세)

Specifications를 사용하여 복잡한 쿼리를 동적으로 생성할 수 있다. Specification 인터페이스를 구현하여 조건을 정의하고, JpaSpecificationExecutor 인터페이스를 통해 사용할 수 있다.

public class UserSpecification implements Specification<User> {
    private String lastName;
    public UserSpecification(String lastName) {
        this.lastName = lastName;
    }

    @Override
    public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
        return criteriaBuilder.equal(root.get("lastName"), lastName);
    }
}

public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
}

// ...


@Autowired
private UserRepository userRepository;

public void findUsers() {
    Specification<User> spec = new UserSpecification("Smith");
    List<User> users = userRepository.findAll(spec);
}

 

사용자 정의 리포지토리 구현

사용자 정의 메소드를 리포지토리에 추가하고 구현할 수 있다. SimpleJpaRepository를 상속받아 사용자 정의 리포지토리를 작성한다.

public interface CustomUserRepository {
    List<User> findCustomUsers(String criteria);
}

public class CustomUserRepositoryImpl implements CustomUserRepository {
    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public List<User> findCustomUsers(String criteria) {
        // Custom query implementation
        return entityManager.createQuery("SELECT u FROM User u WHERE u.criteria = :criteria", User.class)
                            .setParameter("criteria", criteria)
                            .getResultList();
    }
}

public interface UserRepository extends JpaRepository<User, Long>, CustomUserRepository {
}

 

 

반응형