이 가이드는 Spring Data JPA를 사용하여 관계형 데이터베이스에 데이터를 저장하고 검색하는 애플리케이션을 구축하는 과정을 안내합니다.
메모리 기반 데이터베이스에 Customer
POJO(Plain Old Java Objects)를 저장하는 애플리케이션을 구축합니다.
이 예에서는 각각 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
, firstName
및 lastName
의 세 가지 속성이 있는 Customer
클래스가 있습니다. 또한 두 개의 생성자가 있습니다. 기본(default) 생성자는 JPA를 위해서만 존재합니다. 직접 사용하지 않으니 보호됨(protected
)으로 지정되어 있습니다. 다른 생성자는 데이터베이스에 저장할 Customer
인스턴스를 만드는 데 사용하는 생성자입니다.
Customer
클래스에는 @Entity
라는 주석이 붙어 JPA 엔터티임을 나타냅니다. (@Table
주석이 존재하지 않으므로 이 엔터티는 Customer
라는 테이블에 매핑된 것으로 가정합니다.)
JPA가 이를 객체의 ID로 인식할 수 있도록 Customer
객체의 id
속성에는 @Id
라는 주석이 붙습니다. id
속성에는 @GeneratedValue
주석이 추가되어 ID가 자동으로 생성되어야 함을 나타냅니다.
다른 두 속성인 firstName
과 lastName
은 주석이 추가되지 않은 상태로 유지됩니다. 속성 자체와 동일한 이름을 공유하는 열에 매핑된다고 가정합니다.
편리한 toString()
메소드는 고객의 속성을 인쇄합니다.
Java에서 클래스에 명시적 생성자가 없을 때, 컴파일러는 기본 생성자를 자동으로 추가합니다. 그러나 클래스에 다른 생성자가 이미 선언되어 있다면, 명시적으로 생성자를 선언하거나 기본 생성자를 추가해야 합니다.
여기서 protected Customer() {}
는 기본 생성자를 명시적으로 선언한 것입니다. 이렇게 하지 않으면 기본적으로 제공되는 매개변수 없는 생성자인 기본 생성자가 public이 아니라 protected 또는 private으로 제한될 수 있습니다. 그렇게 되면 외부에서 해당 생성자를 사용할 수 없게 됩니다.
JPA에서는 엔티티 클래스가 매개변수 없는 public 또는 protected 생성자를 가져야 합니다. 이는 JPA가 객체를 올바르게 생성하고 관리하기 위해 필요합니다. 만약 명시적으로 생성자를 선언하지 않으면 기본 생성자를 컴파일러가 추가하지만, 여기서 protected Customer() {}
와 같이 생성자를 선언하는 것이 좋은 습관입니다.
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);
}
CustomerRepository
는 CrudRepository
인터페이스를 확장합니다. 작업하는 엔터티 유형과 ID인 Customer
및 Long
은 CrudRepository
의 generic 매개변수에 지정됩니다. CrudRepository
를 확장함으로써 CustomerRepository
는 Customer
엔터티를 저장, 삭제 및 찾는 방법을 포함하여 Customer
persistence 작업을 위한 여러 방법을 상속합니다.
Spring Data JPA를 사용하면 메소드 signature를 선언하여 다른 쿼리 메소드를 정의할 수도 있습니다. 예를 들어 CustomerRepository
에는 findByLastName()
메서드가 포함되어 있습니다.
일반적인 Java 애플리케이션에서는 CustomerRepository
를 구현하는 클래스를 작성할 것으로 예상할 수 있습니다. 그러나 이것이 Spring Data JPA를 매우 강력하게 만드는 이유입니다. 저장소 인터페이스의 구현을 작성할 필요가 없습니다. Spring Data JPA는 애플리케이션을 실행할 때 구현을 생성합니다.
이제 이 예제를 연결하고 어떻게 보이는지 확인할 수 있습니다!
Spring Data JPA에서 CrudRepository
와 JpaRepository
는 둘 다 JPA 기능을 제공하는 인터페이스입니다. 하지만 각각의 차이점이 있습니다.
CrudRepository: 기본적인 CRUD(Create, Read, Update, Delete) 기능을 제공하는 인터페이스입니다. 이 인터페이스는 CRUD 메서드를 제공하여 데이터베이스의 엔티티를 다루는 기본적인 작업을 할 수 있도록 도와줍니다.
JpaRepository: JpaRepository
는 CrudRepository
의 기능을 상속하면서 추가적으로 JPA에 특화된 메서드들을 제공합니다. 이 인터페이스는 PagingAndSortingRepository
를 상속하며, 페이징과 정렬 기능을 제공하고 일반적인 CRUD 외에도 JPA에서 제공하는 다양한 기능들을 사용할 수 있도록 합니다. JpaRepository
는 보다 더 확장된 JPA 관련 기능을 사용할 수 있게 해줍니다.
따라서 기본적인 CRUD 작업만 필요한 경우에는 CrudRepository
를 사용할 수 있고, JPA와 관련된 더 많은 기능이 필요한 경우에는 JpaRepository
를 사용하는 것이 좋습니다.
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-safebasePackageClasses=MyRepository.class
매개변수를 사용하여 대체 패키지를 가리킬 수 있습니다.
main
메서드의 args
매개변수는 프로그램이 실행될 때 외부에서 전달되는 인자를 담고 있는 문자열 배열입니다. 이 배열은 외부에서 프로그램에 전달된 인자들을 담고 있으며, 예를 들어 프로그램 실행 시에 커맨드 라인에서 전달되는 인자들이 이 배열에 저장됩니다.
main
함수에서 args
매개변수가 사용되지 않는 이유는 해당 애플리케이션이 외부에서 인자를 받아 처리하지 않는다는 것을 의미할 수 있습니다. Spring Boot 애플리케이션에서는 SpringApplication.run
을 호출할 때 인자를 전달하지 않아도 정상적으로 애플리케이션이 실행됩니다. 그래서 여기서는 args
를 사용하지 않았을 가능성이 있습니다.
CommandLineRunner
는 run
메서드를 정의한 함수형 인터페이스입니다. 여기서 람다 표현식 (args) -> { ... }
는 CommandLineRunner
의 run
메서드를 오버라이드하는 함수이므로 내부적으로 이 매개변수를 사용하는 것이 일반적입니다. 그러나 여기서는 args
매개변수를 사용하지 않아도 람다 표현식이 잘 작동할 수 있습니다. 이는 args
를 사용하지 않아도 될 때, 즉 해당 람다 표현식 내부에서 외부 매개변수를 참조할 필요가 없는 경우에 해당합니다. 따라서 args
매개변수를 사용하지 않더라도 run
메서드의 시그니처를 따르는 람다 표현식을 사용할 수 있습니다.