백엔드/Spring Boot

모던 웹 애플리케이션 개발 - 3. JPA를 이용한 데이터베이스 생성 및 접근 (1)

두개의 문 2023. 8. 21. 19:12

◉ 엔티티 클래스 생성 

 

 ▪︎ 엔티티 클래스 

  - JPA의 @Entity 어노테이션을 사용하는 자바 클래스 

  

 

 

1. JPA와 H2 데이터베이스를 이용하기 위해 pom.xml 파일에 다음의 의존성 추가해야 함 

 

 

 - 라이브러리 추가 시, Gradle 같이 업데이트 해주어야  

  ① pom.xml에서 오른쪽 클릭 - Maven - Update Project 클릭 

 

 

 ② 업데이트할 프로젝트 선택 : 현재 프로젝트 선택 

 

 


2. domain 패키지에 Car 클래스 생성 

@Entity
public class Car {
	
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	// DB 종류에 따라 JPA가 기본키를 자동으로 생성함 
	private long id; 
	
	private String brand;
	private String model;
	private String color;
	private String registerNumber;
	
	@Column(name = "explanation", nullable = false, length = 512)
	private String description;
	
	// * H2에서 식별자의 경우, 백틱 이용
	@Column(name="`year`")
	private int year;
	private int price;
	
	
	// * 생성자 ( 위치는 속성함수 위 ) 
	// 1) 기본 생성자 
	//    : 컬럼의 내용을 차후에 추가할 수도 있으므로, 만들어놓기 
	public Car() {}
	
	// 2) 매개변수 있는 생성자 
	//    : id는 자동생성이므로, id 필드는 제외 
	public Car(String brand, String model, String color, String registerNumber, String description, int year, int price) {
		super();
		this.brand = brand;
		this.model = model;
		this.color = color;
		this.registerNumber = registerNumber;
		this.description = description;
		this.year = year;
		this.price = price;
	}
	
	// * getter 속성함수 
	public long getId() {
		return id;
	}
	public String getBrand() {
		return brand;
	}
	public String getModel() {
		return model;
	}
	public String getColor() {
		return color;
	}
	public String getRegisterNumber() {
		return registerNumber;
	}
	public String getDescription() {
		return description;
	}
	public int getYear() {
		return year;
	}
	public int getPrice() {
		return price;
	}
	
	// * setter 속성함수 
	public void setId(long id) {
		this.id = id;
	}
	public void setBrand(String brand) {
		this.brand = brand;
	}
	public void setModel(String model) {
		this.model = model;
	}
	public void setColor(String color) {
		this.color = color;
	}
	public void setRegisterNumber(String registerNumber) {
		this.registerNumber = registerNumber;
	}
	public void setDescription(String description) {
		this.description = description;
	}
	public void setYear(int year) {
		this.year = year;
	}
	public void setPrice(int price) {
		this.price = price;
	}
}

▪︎ 기본 키

 - @Id 어노테이션으로 정의 

 - @GeneratedValue 어노테이션 : 데이터베이스가 자동으로 ID 생성하도록 지정 

    → 키 생성 전략을 'AUTO' 유형으로 지정할 경우, 

        JPA 공급자가 특정 데이터베이스에 가장 적합한 전략을 선택한다는 의미 

        즉, DB 종류에 따라 JPA가 자동으로 키 생성함 

 

 

 


3. application.yml에 JPA와 H2 데이터베이스에 대한 속성 추가 

   ▪︎ Snippets에 'H2 DB - application.yml'이름으로 저장해놓음 

# jpa 설정 
spring:
  jpa:
    show-sql: true

# h2 데이터베이스 설정 
  datasource:
    # jdbc : jdbc 프로토콜 의미 
    # h2 : jdbc 데이터베이스 종류
    # mem : local memory로, 로컬 서버를 의미 
    # testdb : 파일 데이터베이스를 의미 
    # 스프링 부트를 중지하면, 데이터 사라짐  
    url: jdbc:h2:mem:testdb

 - 위와 같이 설정하게 되면, 콘솔창에서 SQL문을 확인할 수 있음 

 

※ application.yml 편집 시 주의 사항

   - 행 끝에 공백이 추가되지 않게 주의해야 함 

   - 행 끝에 공백이 있을 경우, 설정이 적용되지 않음 

 

 

▪︎ H2 데이터베이스에는 데이터베이스 관리 및 SQL문을 실행할 수 있는 웹 기반 콘솔 존재 

  이 콘솔을 이용하기 위해서는 아래와 같이 속성 추가해주어야 함 

  # 웹으로 h2 데이터베이스 관리 가능   
  h2:
    console:
      # H2 콘솔 활성화 
      enabled: true  
      # http://localhost/h2-console에서 웹으로 h2 데이터베이스 관리 가능 (경로 지정)
      path: /h2-console

 

 

 


4. CRUD 리포지터리 만들기 

 

▪︎ domain 패키지에 CarRepository 클래스 생성 후, 스프링 부트 JPA의 CrudRepository 인터페이스를 확장함 

 

 ※ < 엔티티 클래스 이름, PK 데이터 타입 >
     단, PK 데이터 타입은 wrapper 클래스명으로 넘겨주어야 함 
     ( 예 : long이 아닌 Long ) 

public interface CarRepository extends CrudRepository<Car, Long>{
}

 

▪︎ CrudRepository 인터페이스는 여러 CRUD 메서드 제공함 

메서드 설명 
long count() 엔티티의 수를 반환 
Iterable<T> findAll() 지정한 형식의 모든 항목을 반환 
Optional<T> findById(ID Id) 지정한 ID의 한 항목을 반환 
void delete(T entity) 엔티티 삭제 
void deleteAll() 리포지터리의 모든 엔티티 삭제 
<S extends T> save(S entity) 엔티티를 저장 
List<S> saveAll(Iterable<S> entities) 여러 엔티티를 저장 

 

 ❶ Iterable 인터페이스

   - Collection 인터페이스의 상위 인터페이스 

 

   - Iterable 인터페이스 안에는 iterator() 메서드가 추상메서드로 선언되어 있음 

      즉, Iterable의 역할 : iterator() 메서드를 하위 클래스에서 무조건 구현하게 만들기 위함 

   - iterator() 메서드를 이용해 Iterator 객체 얻은 , hasNext() next() 메서드를 이용해 컬렉션의 요소를 가져오게  

 

 

 

❷ save() 메서드

  - 기존 데이터가 있다면, update로 작용 

  - 기존 데이터가 없다면, insert 작용 

 


5. H2 데이터베이스에 약간의 예제 데이터 추가하기 

 

▪︎ 스프링 부트 CommandLineRunner 인터페이스 이용 

  - 애플리케이션이 완전히 시작되기 전에 추가 코드를 실행할 수 있어 예제 데이터를5 준비하기에 적합함

  - 즉, 스프링이 로드될 때 특정 코드를 실행할 수 있음 

    CommandLineRunner를 구현한 구현체는 컴포넌트 스캔이 되고, 스프링 부트가 구동되는 시점에 run() 메서드를 실행하게 됨 

    • 이 때, run() 메서드 안의 ...args는 스프링 부트 구동 시, 실행 인자가 있다면, 받아서 처리하게 됨 

 

 

  - 스프링 부트 애플리케이션의 main 클래스가 CommandLineRunner 인터페이스를 구현하게 되면, 오류 발생 

 

 

  - CommandLineRunner 인터페이스의 추상메서드로 선언되어있는 run() 메서드를 구현해야 함 

 

 

 

 

▪︎ CarRepository를 의존성 주입을 이용해 main 클래스에 주입한 후, run() 메서드에서 CRUD 메서드 이용 가능 

 

 

▪︎ main 클래스에 로거 추가 

 ※ Logger import 시, java가 아닌 slf4j 패키지를 선택해야 함 

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

 

 

▪︎ save() 메서드로 여러 자동차 레코드를 데이터베이스에 추가한 후,

   리포지터리의 findAll() 메서드로 데이터베이스에서 모든 자동차 레코드 검색하고, 로거를 통해 콘솔에 출력해보자

@SpringBootApplication
public class DemoApplication implements CommandLineRunner {
	// * @Slf4j 어노테이션의 실제 구현의 예시 
	private static final Logger logger = LoggerFactory.getLogger(DemoApplication.class);
	
	// CommandLineRunner의 run 메서드에서 사용할 carRepository 멤버 변수를 선언 ( 의존성 주입 이용 )
	@Autowired
	private CarRepository repository;
	
	
	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
		logger.info("Application started");
	}

	@Override
	public void run(String... args) throws Exception {
		// * 임시 초기 데이터를 
		//   jpa의 레포지토리 인터페이스를 상속받아야 함 
		// * 데이터 추가 : CrudRepository의 save() 메서드 이용 
		repository.save(
				new Car("Ford", "Mustang", "Red", "ADF-1121", "", 2021, 59000));
		repository.save(
				new Car("Nissan", "Leaf", "White", "SSJ-3002", "", 2019, 29000));
		repository.save(
				new Car("Toyota", "Prius", "Silver", "KKO-0212", "", 2020, 39000));
		
		// * 모든 자동차를 가져와 콘솔에 로깅 
		for( Car car : repository.findAll()) {
			logger.info(car.getBrand() + " " + car.getModel());
		}
        }  
}

 

 - 콘솔에서 확인 

 

 

 - H2 데이터베이스에서 확인