Guide_Accessing Data with JPA

Dev.Hammy·2023년 12월 17일
0

Spring Guides

목록 보기
17/46

이 가이드는 Spring Data JPA를 사용하여 관계형 데이터베이스에 데이터를 저장하고 검색하는 애플리케이션을 구축하는 과정을 안내합니다.

What You Will Build

메모리 기반 데이터베이스에 Customer POJO(Plain Old Java Objects)를 저장하는 애플리케이션을 구축합니다.

Starting with Spring Initializr

Define a Simple Entity

이 예에서는 각각 JPA 엔터티로 주석이 달린 Customer 개체를 저장합니다. 다음 목록은 Customer 클래스(src/main/java/guides/accessingdatajpa/Customer.java에 있음)를 보여줍니다.

package guides.accessingdatajpa;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String firstName;
    private String lastName;

    protected Customer(){}

    public Customer(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    @Override
    public String toString() {
        return String.format(
                "Customer[id = %d, firstName ='%s', lastName = '%s']",
                id, firstName, lastName
        );
    }

    public Long getId() {
        return id;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }
    
}

여기에는 id, firstNamelastName의 세 가지 속성이 있는 Customer 클래스가 있습니다. 또한 두 개의 생성자가 있습니다. 기본(default) 생성자는 JPA를 위해서만 존재합니다. 직접 사용하지 않으니 보호됨(protected)으로 지정되어 있습니다. 다른 생성자는 데이터베이스에 저장할 Customer 인스턴스를 만드는 데 사용하는 생성자입니다.

Customer 클래스에는 @Entity라는 주석이 붙어 JPA 엔터티임을 나타냅니다. (@Table 주석이 존재하지 않으므로 이 엔터티는 Customer라는 테이블에 매핑된 것으로 가정합니다.)

JPA가 이를 객체의 ID로 인식할 수 있도록 Customer 객체의 id 속성에는 @Id라는 주석이 붙습니다. id 속성에는 @GeneratedValue 주석이 추가되어 ID가 자동으로 생성되어야 함을 나타냅니다.

다른 두 속성인 firstNamelastName은 주석이 추가되지 않은 상태로 유지됩니다. 속성 자체와 동일한 이름을 공유하는 열에 매핑된다고 가정합니다.

편리한 toString() 메소드는 고객의 속성을 인쇄합니다.


Java에서 클래스에 명시적 생성자가 없을 때, 컴파일러는 기본 생성자를 자동으로 추가합니다. 그러나 클래스에 다른 생성자가 이미 선언되어 있다면, 명시적으로 생성자를 선언하거나 기본 생성자를 추가해야 합니다.

여기서 protected Customer() {}는 기본 생성자를 명시적으로 선언한 것입니다. 이렇게 하지 않으면 기본적으로 제공되는 매개변수 없는 생성자인 기본 생성자가 public이 아니라 protected 또는 private으로 제한될 수 있습니다. 그렇게 되면 외부에서 해당 생성자를 사용할 수 없게 됩니다.

JPA에서는 엔티티 클래스가 매개변수 없는 public 또는 protected 생성자를 가져야 합니다. 이는 JPA가 객체를 올바르게 생성하고 관리하기 위해 필요합니다. 만약 명시적으로 생성자를 선언하지 않으면 기본 생성자를 컴파일러가 추가하지만, 여기서 protected Customer() {}와 같이 생성자를 선언하는 것이 좋은 습관입니다.


Create Simple Queries

Spring Data JPA는 JPA를 사용하여 관계형 데이터베이스에 데이터를 저장하는 데 중점을 둡니다. 가장 강력한 기능은 저장소 인터페이스에서 런타임 시 자동으로 저장소 구현을 생성하는 기능입니다.

이것이 어떻게 작동하는지 보려면 다음 목록(src/main/java/guides/accessingdatajpa/CustomerRepository.java)에 표시된 대로 Customer 엔터티와 작동하는 저장소 인터페이스를 만듭니다.

package guides.accessingdatajpa;

import org.springframework.data.repository.CrudRepository;

import java.util.List;

public interface CustomerRepository extends CrudRepository<Customer, Long> {
    
    List<Customer> findByLastName(String lastName);
    Customer findById(long id);
}

CustomerRepositoryCrudRepository 인터페이스를 확장합니다. 작업하는 엔터티 유형과 ID인 CustomerLongCrudRepository의 generic 매개변수에 지정됩니다. CrudRepository를 확장함으로써 CustomerRepositoryCustomer 엔터티를 저장, 삭제 및 찾는 방법을 포함하여 Customer persistence 작업을 위한 여러 방법을 상속합니다.

Spring Data JPA를 사용하면 메소드 signature를 선언하여 다른 쿼리 메소드를 정의할 수도 있습니다. 예를 들어 CustomerRepository에는 findByLastName() 메서드가 포함되어 있습니다.

일반적인 Java 애플리케이션에서는 CustomerRepository를 구현하는 클래스를 작성할 것으로 예상할 수 있습니다. 그러나 이것이 Spring Data JPA를 매우 강력하게 만드는 이유입니다. 저장소 인터페이스의 구현을 작성할 필요가 없습니다. Spring Data JPA는 애플리케이션을 실행할 때 구현을 생성합니다.

이제 이 예제를 연결하고 어떻게 보이는지 확인할 수 있습니다!


Spring Data JPA에서 CrudRepositoryJpaRepository는 둘 다 JPA 기능을 제공하는 인터페이스입니다. 하지만 각각의 차이점이 있습니다.

  1. CrudRepository: 기본적인 CRUD(Create, Read, Update, Delete) 기능을 제공하는 인터페이스입니다. 이 인터페이스는 CRUD 메서드를 제공하여 데이터베이스의 엔티티를 다루는 기본적인 작업을 할 수 있도록 도와줍니다.

  2. JpaRepository: JpaRepositoryCrudRepository의 기능을 상속하면서 추가적으로 JPA에 특화된 메서드들을 제공합니다. 이 인터페이스는 PagingAndSortingRepository를 상속하며, 페이징과 정렬 기능을 제공하고 일반적인 CRUD 외에도 JPA에서 제공하는 다양한 기능들을 사용할 수 있도록 합니다. JpaRepository는 보다 더 확장된 JPA 관련 기능을 사용할 수 있게 해줍니다.

따라서 기본적인 CRUD 작업만 필요한 경우에는 CrudRepository를 사용할 수 있고, JPA와 관련된 더 많은 기능이 필요한 경우에는 JpaRepository를 사용하는 것이 좋습니다.


Create an Application Class

Spring 초기화는 애플리케이션을 위한 간단한 클래스를 생성합니다. 다음 목록은 이 예제를 위해 Initializr가 생성한 클래스(src/main/java/guides/accessingdatajpa/AccessingDataJpaApplication.java)를 보여줍니다.

package guides.accessingdatajpa;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class AccessingDataJpaApplication {

  private static final Logger log = LoggerFactory.getLogger(AccessingDataJpaApplication.class);

  public static void main(String[] args) {
    SpringApplication.run(AccessingDataJpaApplication.class);
  }

  @Bean
  public CommandLineRunner demo(CustomerRepository repository) {
    return (args) -> {
      // save a few customers
      repository.save(new Customer("Jack", "Bauer"));
      repository.save(new Customer("Chloe", "O'Brian"));
      repository.save(new Customer("Kim", "Bauer"));
      repository.save(new Customer("David", "Palmer"));
      repository.save(new Customer("Michelle", "Dessler"));

      // fetch all customers
      log.info("Customers found with findAll():");
      log.info("-------------------------------");
      repository.findAll().forEach(customer -> {
        log.info(customer.toString());
      });
      log.info("");

      // fetch an individual customer by ID
      Customer customer = repository.findById(1L);
      log.info("Customer found with findById(1L):");
      log.info("--------------------------------");
      log.info(customer.toString());
      log.info("");

      // fetch customers by last name
      log.info("Customer found with findByLastName('Bauer'):");
      log.info("--------------------------------------------");
      repository.findByLastName("Bauer").forEach(bauer -> {
        log.info(bauer.toString());
      });
      log.info("");
    };
  }

}

AccessingDataJpaApplication 클래스에는 몇 가지 테스트를 통해 CustomerRepository를 실행하는 demo() 메서드가 포함되어 있습니다. 먼저 Spring 애플리케이션 컨텍스트에서 CustomerRepository를 가져옵니다. 그런 다음 소수의 Customer 개체를 저장하고 save() 메서드를 시연하고 작업할 일부 데이터를 설정합니다. 다음으로 findAll()을 호출하여 데이터베이스에서 모든 Customer 개체를 가져옵니다. 그런 다음 findById()를 호출하여 해당 ID로 단일 Customer를 가져옵니다. 마지막으로 findByLastName()을 호출하여 성이 "Bauer"인 모든 고객을 찾습니다. demo() 메소드는 애플리케이션이 시작될 때 자동으로 코드를 실행하는 CommandLineRunner Bean을 반환합니다.

기본적으로 Spring Boot는 JPA 저장소 지원을 활성화하고 @SpringBootApplication이 있는 패키지(및 해당 하위 패키지)를 찾습니다. 구성에 표시되지 않는 패키지에 JPA 저장소 인터페이스 정의가 있는 경우 @EnableJpaRepositories 및 해당 type-safe basePackageClasses=MyRepository.class 매개변수를 사용하여 대체 패키지를 가리킬 수 있습니다.


main 메서드의 args 매개변수는 프로그램이 실행될 때 외부에서 전달되는 인자를 담고 있는 문자열 배열입니다. 이 배열은 외부에서 프로그램에 전달된 인자들을 담고 있으며, 예를 들어 프로그램 실행 시에 커맨드 라인에서 전달되는 인자들이 이 배열에 저장됩니다.

  1. main 함수에서 args 매개변수가 사용되지 않는 이유는 해당 애플리케이션이 외부에서 인자를 받아 처리하지 않는다는 것을 의미할 수 있습니다. Spring Boot 애플리케이션에서는 SpringApplication.run을 호출할 때 인자를 전달하지 않아도 정상적으로 애플리케이션이 실행됩니다. 그래서 여기서는 args를 사용하지 않았을 가능성이 있습니다.

  2. CommandLineRunnerrun 메서드를 정의한 함수형 인터페이스입니다. 여기서 람다 표현식 (args) -> { ... }CommandLineRunnerrun 메서드를 오버라이드하는 함수이므로 내부적으로 이 매개변수를 사용하는 것이 일반적입니다. 그러나 여기서는 args 매개변수를 사용하지 않아도 람다 표현식이 잘 작동할 수 있습니다. 이는 args를 사용하지 않아도 될 때, 즉 해당 람다 표현식 내부에서 외부 매개변수를 참조할 필요가 없는 경우에 해당합니다. 따라서 args 매개변수를 사용하지 않더라도 run 메서드의 시그니처를 따르는 람다 표현식을 사용할 수 있습니다.

0개의 댓글