스프링 부트에 Spring Data JPA라이브러리를 사용하여 JPA를 세팅하는 방법을 알아보자.
개발환경
Spring Boot: 3.2.0-SNAPSHOT
Gradle: gradle-8.4
Java: OpenJDK17
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
}
# 아래 데이터 추가
logging.level.org.hibernate.SQL=DEBUG
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.format_sql=true
만약 application.properties가 아닌 yml이나 xml 파일을 사용한다면 해당 파일의 형식만 맞춰주면 된다.
옵션을 하나하나 살펴보자면
Logger에서 SQL 쿼리문을 출력할 로그 레벨을 나타낸다.
TRACE < DEBUG < INFO < WARN < ERROR
위 순서대로 중요도가 높다.
로그로 실행되는 쿼리문을 출력할지 여부를 나타낸다.
default는 false
DDL (Data Definition Language, 데이터 정의어)를 자동생성할지 여부를 나타낸다.
즉 @Entity, @Table, @Column 등 데이터의 정의를 나타내는 어노테이션이 붙은 객체와 속성에 대한 DDL 쿼리를 빌드시에 자동으로 생성 시킬지 여부이다.
기본적으로
@Entity어노테이션에name속성이 없다면 class명으로 생성되고 Entity명을 다르게 주고 싶다면 name 속성을 준다.만약 Entity 명과 DB에 들어가는 Table명을 다르게 설정하고 싶다면
@Table어노테이션을 사용해name속성을 줌으로써 설정할 수 있다. (아래 3번 참고)
종류는 다음과 같다.
create
엔티티로 등록된 클래스와 매핑되는 테이블을 자동으로 생성한다. 기존에 해당 클래스와 매핑되는 테이블이 존재한다면 기존 테이블을 삭제하고 테이블을 생성한다.create-drop
엔티티로 등록된 클래스와 매핑되는 테이블이 존재한다면 기존 테이블을 삭제하고 자동으로 테이블을 생성해주는 것은 똑같지만, 애플리케이션이 종료될 때 테이블을 삭제한다.update
엔티티로 등록된 클래스와 매핑되는 테이블이 없으면 새로 생성하는 것은 동일하지만 기존 테이블이 존재한다면 위의 두 경우와 달리 테이블의 컬럼을 변경하게 된다.주의할 점은
update의 경우 테이블과 칼럼은 자동으로 수정 되지만 속성의 경우 update 되지 않을 수 있다.validate
DDL을 작성하여 테이블을 생성하거나 수정하지 않고, 엔티티 클래스와 테이블이 정상적으로 매핑되는지만 검사한다. 만약 테이블이 아예 존재하지 않거나, 테이블에 엔티티의 필드에 매핑되는 컬럼이 존재하지 않으면 예외를 발생시키면서 애플리케이션을 종료한다.none(default)
DDL 자동 생성 안함
이 기능은 개발을 용이하게 하기 위함이기 때문에 개발환경에서만 사용하고 실제 운영서버의 경우 주의해서 사용할 필요가 있다. 운영환경에서는 validate 또는 none으로 설정하는 것이 좋다.
쿼리를 formatting할 지 여부를 나타낸다.
만약 false로 설정되면 쿼리는 한줄로 로그에 표시되고 true로 설정되면 보기 예쁘게 줄바꿈되어 나온다.
// /com/practice/project/test/entity/TestEntity.java
package com.practice.project.test.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import lombok.*;
@Builder
@Entity(name = "test_entity")
public class TestEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String val1;
@Column
private String val2;
}
// /com/practice/project/test/repository/TestRepository.java
package com.practice.project.test.repository;
import com.practice.project.test.entity.TestEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
// JpaRepository를 상속하여 사용. <객체, ID타입>
@Repository
public interface TestRepository extends JpaRepository<TestEntity, Long> {
}
이렇게 설정하면 JpaRepository에서 제공하는 기본 적인 쿼리와 쿼리 양식을 사용할 수 있다.
// /com/practice/project/test/controller/TestController.java
package com.practice.project.test.controller;
import com.practice.project.test.repository.TestRepository;
import com.practice.project.test.entity.TestEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;
import java.util.List;
// JpaRepository를 상속하여 repository (interface) 생성. <객체, ID타입>
@RestController
@RequiredArgsConstructor
public class TestController {
// 여기서는 서비스까지 생성하기는 귀찮아서 repository를 그대로 사용했지만
// 실제로는 이렇게 사용하면 안된다.
// 3-tier 구조를 지켜야한다!
private final TestRepository testRepository;
@PostMapping("/test")
public ResponseEntity<TestEntity> createTest() {
TestEntity testEntity = testRepository.save(
TestEntity.builder()
.val1("val1")
.val2("val2")
.build()
);
return ResponseEntity.ok(testEntity);
}
@GetMapping("/tests")
@ResponseStatus(HttpStatus.OK)
public List<TestEntity> getTestList() {
return testRepository.findAll();
}
}