Note-Taking / / 2024. 10. 28. 14:40

Spring Data JPA 정리

목차

1. Getting Started: Spring Data JPA 프로젝트를 설정하고 시작하는 기초적인 단계로, 의존성 추가와 기본 설정 등을 포함합니다.

2. Core Concepts: JPA의 주요 개념인 엔티티, 영속성 컨텍스트, 트랜잭션 등 데이터 영속성과 관련된 핵심 개념을 다룹니다.

3. Defining Repository Interfaces: 데이터베이스 작업을 위한 인터페이스인 JpaRepository CrudRepository 인터페이스를 정의하고 사용하는 방법을 다룹니다.

4. Configuration: Spring Data JPA의 설정 방법을 설명하며, application.properties 파일을 통한 설정과 관련된 내용입니다.

5. Persisting Entities: 엔티티를 데이터베이스에 저장하는 방법과 관련된 개념입니다. save 메서드를 사용해 영속화하는 방식을 다룹니다.

6. Defining Query Methods: 메서드 이름을 기반으로 자동으로 SQL 쿼리를 생성하는 방식으로, 커스텀 쿼리 메서드를 정의하는 방법입니다.

7. JPA Query Methods: @Query 어노테이션을 사용하여 JPQL(Java Persistence Query Language)을 직접 작성하여 쿼리를 실행하는 방법을 설명합니다.

8. Projections: 엔티티의 특정 필드만 조회하여 반환하는 방법으로, DTO나 인터페이스 기반의 프로젝션을 사용합니다.

9. Stored Procedures: 데이터베이스에 저장된 프로시저를 호출하여 복잡한 쿼리를 실행하는 방법을 설명합니다.

10. Specifications: 동적 쿼리를 작성하는 방식으로, 다양한 조건을 조합하여 쿼리를 구성할 수 있는 방법입니다.

11. Query by Example: 예시 객체를 기반으로 조건에 맞는 데이터를 조회하는 방식으로, 복잡한 쿼리 없이 간단한 검색을 수행할 때 사용됩니다.

12. Transactionality: JPA에서 트랜잭션을 관리하는 방식과 @Transactional 어노테이션의 사용법을 설명합니다.

13. Locking: 동시성 문제를 방지하기 위해 엔티티에 대한 비관적 락이나 낙관적 락을 설정하는 방법을 다룹니다.

14. Auditing: 엔티티의 생성일, 수정일 등을 자동으로 기록하는 기능으로, @CreatedDate @LastModifiedDate 등의 어노테이션을 사용합니다.

15. Merging Persistence Units: 여러 퍼시스턴스 유닛을 병합하여 사용할 때의 방법과 설정입니다.

16. CDI Integration: Java의 CDI(Contexts and Dependency Injection)와의 통합 방법을 설명합니다.

17. Custom Repository Implementations: 기본 JPA 기능 외에 커스텀 기능을 추가하기 위해 리포지토리의 구현체를 정의하는 방법입니다.

18. Publishing Events from Aggregate Roots: 엔티티에서 이벤트를 발행하여 도메인 이벤트를 처리하는 방법입니다.

19. Null Handling of Repository Methods: 리포지토리 메서드에서 null을 반환하는 경우와 Optional을 사용하는 방법을 설명합니다.

20. Spring Data Extensions: Spring Data JPA 외에 Spring Data Redis, Spring Data MongoDB 등의 확장 기능을 설명합니다.

21. Repository Query Keywords: 메서드 이름에 사용할 수 있는 키워드로, 자동으로 SQL 쿼리를 생성하는 데 사용됩니다.

22. Repository Query Return Types: 쿼리 메서드의 반환 타입으로, List, Optional, Stream 등 다양한 반환 타입을 설정할 수 있습니다.


1. Getting Started

 

 설명: Spring Data JPA를 사용하려면 프로젝트에 의존성을 추가해야 합니다. spring-boot-starter-data-jpa를 추가하면 Spring Boot가 JPA 관련 설정을 자동으로 처리해줍니다.

 예시:

<!-- pom.xml 파일에 의존성 추가 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

 

 application.properties 파일에 데이터베이스 설정을 추가하여 데이터 소스와 연결합니다.

spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.hibernate.ddl-auto=update

 

 이를 통해 JPA와 데이터베이스 간의 연결이 설정되고, 애플리케이션이 시작될 때 자동으로 JPA 환경이 구성됩니다.

 

2. Core Concepts

 

 설명: JPA의 기본 개념인 엔티티(Entity), 영속성(Persistence), 영속성 컨텍스트(Persistence Context)를 이해해야 합니다.

 예시:

 엔티티: 데이터베이스 테이블과 매핑되는 클래스입니다.

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;
    private String email;

    // Getters, Setters 등
}

 

 영속성 컨텍스트: JPA가 엔티티를 관리하는 공간으로, 엔티티가 이 컨텍스트에 저장되면 변경 사항을 추적하여 자동으로 데이터베이스에 반영합니다.

 

3. Defining Repository Interfaces

 

 설명: 데이터베이스에 접근하기 위한 리포지토리 인터페이스입니다. JpaRepository 또는 CrudRepository를 상속하여 CRUD 메서드를 자동으로 사용할 수 있습니다.

 예시:

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

 

 이렇게 인터페이스만 정의해도 findAll(), save(), deleteById() 등의 기본적인 CRUD 메서드를 사용할 수 있습니다.

 

4. Configuration

 

 설명: Spring Data JPA는 기본적으로 자동 설정을 제공하지만, 특정 요구 사항이 있을 경우 직접 설정을 추가할 수 있습니다.

 예시: application.properties 파일에서 JPA 설정을 수정할 수 있습니다.

spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.hibernate.ddl-auto=update

 

 여기서 show-sql은 SQL 쿼리를 콘솔에 출력하도록 설정하며, ddl-auto는 데이터베이스 스키마를 자동으로 업데이트하도록 설정합니다.

 

5. Persisting Entities

 

 설명: save 메서드를 사용하여 엔티티를 데이터베이스에 저장할 수 있습니다. 엔티티가 영속성 컨텍스트에 저장되면, JPA가 해당 엔티티의 상태를 관리하게 됩니다.

 예시:

User user = new User();
user.setUsername("john");
user.setEmail("john@example.com");

userRepository.save(user); // 엔티티를 데이터베이스에 저장

 

 save 메서드는 새로운 엔티티를 데이터베이스에 삽입하거나, 기존 엔티티를 업데이트합니다.

 

6. Defining Query Methods

 

 설명: 리포지토리 인터페이스의 메서드 이름을 통해 쿼리를 자동으로 생성할 수 있습니다.

 예시:

public interface UserRepository extends JpaRepository<User, Long> {
    List<User> findByUsername(String username); // 메서드 이름을 통해 쿼리 생성
}

 

 findByUsername 메서드는 username 필드를 조건으로 하여 해당 조건을 만족하는 엔티티 리스트를 반환합니다.

 

7. JPA Query Methods

 

 설명: 메서드 이름으로 쿼리를 정의하는 대신, @Query 어노테이션을 사용해 JPQL(Java Persistence Query Language)로 직접 쿼리를 작성할 수 있습니다.

 예시:

@Query("SELECT u FROM User u WHERE u.email = :email")
User findUserByEmail(@Param("email") String email);

 

 @Query를 사용하면 더 복잡한 조건이나 조인을 적용한 쿼리를 작성할 수 있습니다.

 

8. Projections

 

 설명: 전체 엔티티를 가져오는 대신 필요한 필드만 조회하여 성능을 향상시킬 수 있습니다.

 예시:

 인터페이스 기반 프로젝션:

public interface UserProjection {
    String getUsername();
    String getEmail();
}

 

 리포지토리 메서드에서 특정 필드만 선택하는 방법:

List<UserProjection> findAllBy();

 

 이를 통해 엔티티의 특정 필드만 조회하여 네트워크와 메모리 사용을 최적화할 수 있습니다.

 

9. Stored Procedures

 

 설명: 저장 프로시저는 데이터베이스 내에 미리 작성된 SQL 코드로, 복잡한 작업을 효율적으로 수행할 수 있습니다.

 예시:

@Procedure("calculate_salary")
Integer calculateSalary(@Param("employee_id") Long employeeId);

 

 @Procedure를 사용하여 저장 프로시저를 호출할 수 있으며, 대량 데이터 처리나 복잡한 연산을 데이터베이스 레벨에서 실행할 수 있습니다.

 

10. Specifications

 

 설명: 동적 쿼리를 작성할 수 있는 기능으로, Specification 인터페이스를 구현하여 다양한 조건을 조합할 수 있습니다.

 예시:

Specification<User> hasName(String name) {
    return (root, query, cb) -> cb.equal(root.get("name"), name);
}

Specification<User> hasEmail(String email) {
    return (root, query, cb) -> cb.equal(root.get("email"), email);
}

// 두 가지 조건을 합쳐서 사용할 수 있음
List<User> users = userRepository.findAll(hasName("john").and(hasEmail("john@example.com")));

 

 Specification을 통해 동적으로 조건을 추가하거나 조합할 수 있어, 복잡한 쿼리도 쉽게 작성할 수 있습니다.

 

11. Query by Example

 

 설명: Query by Example은 예시 객체를 기반으로 조건에 맞는 데이터를 조회하는 기능으로, 간단한 조건을 사용하여 검색할 때 유용합니다. 동적 쿼리를 사용하지 않고, 샘플 엔티티 객체를 제공해 자동으로 매칭되는 데이터를 찾습니다.

 예시:

// 검색 조건으로 사용할 샘플 엔티티
User userExample = new User();
userExample.setUsername("john");

Example<User> example = Example.of(userExample); // Example 객체 생성
List<User> users = userRepository.findAll(example); // 조건에 맞는 엔티티 조회

 

 이 방식은 특정 조건을 설정하지 않고 예제 객체의 필드 값만으로 검색 조건을 설정하므로, 간단한 검색 작업에 적합합니다.

 

12. Transactionality

 

 설명: 트랜잭션은 데이터의 일관성을 유지하기 위해 여러 데이터베이스 작업을 하나의 작업으로 묶어 수행하는 것을 의미합니다. @Transactional 어노테이션을 사용하여 트랜잭션을 관리할 수 있습니다.

 예시:

@Transactional
public void updateUser(Long userId, String newEmail) {
    User user = userRepository.findById(userId).orElseThrow();
    user.setEmail(newEmail);
    userRepository.save(user); // 메서드가 종료되기 전까지 트랜잭션 유지
}

 

 @Transactional이 설정된 메서드는 성공적으로 끝나면 변경 사항이 커밋되고, 예외가 발생하면 롤백됩니다.

 

13. Locking

 

 설명: 동시성 문제를 방지하기 위해 데이터베이스에서 비관적(Pessimistic) 락 낙관적(Optimistic) 락을 사용할 수 있습니다. 락을 사용하여 데이터의 무결성을 보호합니다.

 예시:

 낙관적 락 @Version 어노테이션을 사용하여 버전 관리를 합니다. 예를 들어, 버전이 일치하지 않으면 업데이트가 실패하게 합니다.

@Entity
public class Product {
    @Id
    private Long id;

    @Version
    private Integer version; // 버전 필드로 낙관적 락 관리
}

 

 비관적 락은 쿼리 메서드에 @Lock 어노테이션을 추가해 락을 설정합니다.

@Lock(LockModeType.PESSIMISTIC_WRITE)
Product findById(Long id);
@Entity
@EntityListeners(AuditingEntityListener.class)
public class User {

    @Id
    @GeneratedValue
    private Long id;

    @CreatedDate
    private LocalDateTime createdDate;

    @LastModifiedDate
    private LocalDateTime lastModifiedDate;
}

 비관적 락은 데이터 충돌을 방지하기 위해 락을 잡아두며, 낙관적 락은 주로 읽기 위주의 작업에서 사용합니다.

 

14. Auditing

 

 설명: 엔티티의 생성일, 수정일, 생성자 등을 자동으로 기록하는 기능입니다. @CreatedDate, @LastModifiedDate, @CreatedBy 어노테이션 등을 사용하여 자동 기록됩니다.

 예시:

@Entity
@EntityListeners(AuditingEntityListener.class)
public class User {

    @Id
    @GeneratedValue
    private Long id;

    @CreatedDate
    private LocalDateTime createdDate;

    @LastModifiedDate
    private LocalDateTime lastModifiedDate;
}

 

 Auditing 기능을 사용하려면 @EnableJpaAuditing을 설정해야 합니다.

 

15. Merging Persistence Units

 

 설명: 여러 개의 퍼시스턴스 유닛(Persistence Unit)을 하나로 병합하여 사용할 때 사용됩니다. 일반적으로 다양한 데이터베이스나 데이터 소스를 사용하는 경우 설정합니다.

 이 주제는 특정한 상황에서만 필요하며, 여러 데이터베이스 연결을 관리하는 복잡한 애플리케이션에서 활용됩니다.

 

16. CDI Integration

 

 설명: CDI(Contexts and Dependency Injection)는 Java EE의 의존성 주입 메커니즘입니다. Spring Data JPA는 CDI와 통합되어 Java EE 환경에서도 사용할 수 있습니다.

 이 기능을 통해 Spring Data 리포지토리를 Java EE 환경에서 생성하고 관리할 수 있으며, Spring이 아닌 환경에서도 데이터 접근 계층을 사용할 수 있습니다.

 

17. Custom Repository Implementations

 

 설명: 기본 제공되는 JpaRepository CrudRepository의 기능 외에 추가로 필요한 기능이 있을 때 사용자 정의 리포지토리 메서드를 구현할 수 있습니다.

 예시:

 UserRepositoryCustom이라는 커스텀 인터페이스를 생성하고, 이를 구현한 클래스에서 추가 메서드를 정의합니다.

public interface UserRepositoryCustom {
    List<User> findUsersByCustomCondition();
}

public class UserRepositoryImpl implements UserRepositoryCustom {
    @Override
    public List<User> findUsersByCustomCondition() {
        // 커스텀 로직 구현
    }
}

 

 UserRepository에서 UserRepositoryCustom을 상속받아 커스텀 메서드를 사용할 수 있습니다.

 

18. Publishing Events from Aggregate Roots

 

 설명: 애그리게이트 루트(Aggregate Root)에서 이벤트를 발행하는 방법입니다. 엔티티에서 도메인 이벤트를 발행하여 도메인 모델의 변경 사항을 다른 컴포넌트에 알릴 수 있습니다.

 예시:

@Entity
public class Order {

    @Id
    private Long id;

    public void placeOrder() {
        // 주문이 완료될 때 이벤트 발행
        DomainEventPublisher.publish(new OrderPlacedEvent(this));
    }
}

 

 도메인 이벤트 발행을 통해 다른 서비스나 모듈에서 이를 구독하여 원하는 동작을 실행할 수 있습니다.

 

19. Null Handling of Repository Methods

 

 설명: Spring Data JPA에서 리포지토리 메서드가 null을 반환하지 않고 Optional을 반환하도록 권장합니다.

 예시:

Optional<User> user = userRepository.findById(1L);
user.ifPresent(u -> System.out.println(u.getUsername()));

 

 Optional을 사용하면 null로 인한 NullPointerException을 방지할 수 있어, 안정성이 높아집니다.

 

20. Spring Data Extensions

 

 설명: Spring Data 프로젝트는 JPA 외에도 Redis, MongoDB, Cassandra, Elasticsearch 등 다양한 데이터베이스와 데이터 소스와의 통합을 지원합니다.

 이를 통해 동일한 리포지토리 스타일을 유지하면서도 다양한 데이터 저장소를 사용할 수 있습니다.

 

21. Repository Query Keywords

 

 설명: Spring Data JPA에서 쿼리 메서드를 정의할 때 사용되는 키워드입니다. findBy, countBy, existsBy 등과 같은 키워드를 통해 다양한 조건의 쿼리를 자동 생성할 수 있습니다.

 예시:

List<User> findByUsername(String username);
long countByStatus(String status);
boolean existsByEmail(String email);

 

 이러한 키워드를 사용하여 여러 조건을 손쉽게 적용할 수 있습니다.

 

22. Repository Query Return Types

 

 설명: Spring Data JPA는 다양한 반환 타입을 지원합니다. 예를 들어, List, Optional, Stream 등의 반환 타입을 사용할 수 있습니다.

 예시:

Optional<User> findById(Long id); // Optional 반환
List<User> findAllByStatus(String status); // List 반환
Stream<User> readAllBy(); // Stream 반환

 

 다양한 반환 타입을 통해 상황에 맞는 데이터를 더 효율적으로 처리할 수 있습니다.

  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유